JAVA/SPRING

[SPRING MVC] Formatter

gracelove91 2019. 10. 14. 22:45

1014 스프링mvc formatter

Formatter ?

  • 문자열을 객체로 변환시키거나 객체를 문자열로 변환해주는 일을 한다.

Person.java

@Getter
@Setter
public class Person {
  private String name;
}

SampleController.java

@RestController
public class SampleController {

    @GetMapping("/hello/{name}")
    public String helloPathVariable(@PathVariable("name") Person person) {
        return "hello this is pathVar. "+person.getName();
    }

    @GetMapping("/hello")
    public String helloRequestParam(@RequestParam("name") Person person) {
        return "hello this is reqParam. "+person.getName();
    }
}

위의 예제는 Formatter 설정이 없으면 에러난다. name 을 적절한 객체로 변환시켜주지 못했기 때문이다.

샘플코드를 제대로 동작시키기위해서 Formatter 설정을 해보자.

PersonFormatter.java

public class PersonFormatter implements Formatter<Person> {
    @Override
    public Person parse(String s, Locale locale) throws ParseException {
        Person person = new Person();
        person.setName(s);
        return person;
    }

    @Override
    public String print(Person person, Locale locale) {
        return person.toString();
    }
}

Formatter를 구현해야한다. 제네릭타입은 문자열을 객체로, 객체를 문자열로 변환시켜 줄 객체타입이 들어가야된다.

public Person parse(String s, Locale locale) throws ParseException { ... } 은 문자열을 객체로 변환시켜 출력할 때 사용되고, public String print(Person person, Locale locale) {...} 은 객체를 문자열로 변환시켜줄 때 필요하다.

Formatter 작성이 끝났으면, 스프링MVC에 달아줘야한다. 어떻게? WebMvcConfigure 를 구현함으로써. 다음 예제를 보자.

WebConfig.java

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addFormatter(new PersonFormatter());
    }
}
  • 스프링레거시를 쓸 때는 이와 같이 public void addFormatters(FormatterRegistry registry) { ... } 메소드를 이용하면된다.

  • 스프링 부트를 쓸 때는 이런 설정없이 Formatter 를 빈으로 등록시켜주면 된다.

    PersonFormatter.java

    @Component
    public class PersonFormatter implements Formatter<Person> {
        @Override
        public Person parse(String s, Locale locale) throws ParseException {
            Person person = new Person();
            person.setName(s);
            return person;
        }
    
        @Override
        public String print(Person person, Locale locale) {
            return person.toString();
        }
    }

테스트코드 작성

SampleControllerTest.java

@RunWith(SpringRunner.class)
@WebMvcTest
public class SampleControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    public void hello() throws Exception {
        mockMvc.perform(get("/hello/eunmo"))
                .andExpect(status().isOk())
                .andDo(print())
                .andExpect(content().string("hello this is pathVar. eunmo"));
    }

    @Test
    public void hello2() throws Exception {
        mockMvc.perform(get("/hello?name=eunmo"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(content().string("hello this is reqParam. eunmo"));
    }

    @Test
    public void hello3() throws Exception {
        mockMvc.perform(get("/hello")
                    .param("name","eunmo"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(content().string("hello this is reqParam. eunmo"));
    }
}

이 테스트코드가 성공하기 위해선 앞서말한 addFormatter메서드를 이용해야한다. 왜냐하면 @WebMvcTest 는 웹과 관련된 빈들만 만들어주기 때문이다.

Formatter 자체를 @Component 를 사용해 빈으로 등록시켜줬다면 @Controller 와 같은 웹과 관련된 빈이 아니므로 테스트가 깨진다. 다음과 같은 방법을 사용하자.

  1. 명시적으로 빈으로 등록한 후 테스트를 한다.

  2. @WebMvcTest 어노테이션 대신 @SpringBootTest 를 사용해 모든 빈을 등록시켜서 테스트를 한다. (이 때는 MockMvc를 주입받을 수 없으므로 @AutoConfigureMockMvc 또한 붙여줘야한다.)

SampleControllerTest.java

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class SampleControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    public void hello() throws Exception {
        mockMvc.perform(get("/hello/eunmo"))
                .andExpect(status().isOk())
                .andDo(print())
                .andExpect(content().string("hello this is pathVar. eunmo"));
    }

    @Test
    public void hello2() throws Exception {
        mockMvc.perform(get("/hello?name=eunmo"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(content().string("hello this is reqParam. eunmo"));
    }

    @Test
    public void hello3() throws Exception {
        mockMvc.perform(get("/hello")
                    .param("name","eunmo"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(content().string("hello this is reqParam. eunmo"));
    }
}