Spring์์ Controller ์์ฑ ์ @RequestParam
๊ณผ @ResponseBody
์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ๊ณค ํ๋๋ฐ, ๊ฐ ์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ๋ ์ด์ ์ ๋์ ๊ณผ์ ์ ๋ํด ์์๋ณด์.
QueryString์ผ๋ก ๋ฐ์ Parameter๋ฅผ ์ ์ํ๋ ์ด๋ ธํ ์ด์ ์ด๋ค.
public @interface RequestParam {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
String defaultValue() default ValueConstants.DEFAULT_NONE;
}
hello-string
์๋ํฌ์ธํธ์์ name
์ ์ธ์๋ก ๋ฐ๊ณ ์ถ๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์์ฑํด ์ฃผ๋ฉด ๋๋ค.
ํ์ ์๊ตฌ์ฌํญ์ด ์๋๋ผ๋ฉด required=false
๋ฅผ ์ถ๊ฐํด์ค๋ค.
@GetMapping("hello-string")
@ResponseBody
public String helloString(@RequestParam("name") String name) {
return "hello " + name;
}
@RequestParam
์ด๋
ธํ
์ด์
์ ์์ฑํ์ง ์์ผ๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
@GetMapping("hello-string")
@ResponseBody
public String helloString(String name) {
return "hello " + name;
}
RequestParamMethodArgumentResolver
์ createNameValueInfo
ํจ์๋ฅผ ํ์ธํด๋ณด๋ฉด RequestParam
์ด๋
ธํ
์ด์
์กด์ฌ ์ฌ๋ถ๋ฅผ ํ์ธํ ํ ์์ผ๋ฉด ์์ฑํด์ค๋ค.
package org.springframework.web.method.annotation;
...
public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver
implements UriComponentsContributor {
...
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
RequestParam ann = parameter.getParameterAnnotation(RequestParam.class);
return (ann != null ? new RequestParamNamedValueInfo(ann) : new RequestParamNamedValueInfo());
}
...
}
ํ์ง๋ง ์ด๋
ธํ
์ด์
์ ์์ฑํ์ง ์์ ์์ฑ๋ RequestParamNamedValueInfo
๊ฐ์ฒด๋ required=false
๊ฐ ๊ธฐ๋ณธ๊ฐ์ด๋ค.
private static class RequestParamNamedValueInfo extends NamedValueInfo {
public RequestParamNamedValueInfo() {
super("", false, ValueConstants.DEFAULT_NONE);
}
public RequestParamNamedValueInfo(RequestParam annotation) {
super(annotation.name(), annotation.required(), annotation.defaultValue());
}
}
Spring ๊ฐ์ฒด๋ฅผ ๋ค์ํ ์๋ต ํ์์ผ๋ก ๋ณํํ์ฌ ๋ฐํํด์ฃผ๋ ์ด๋ ธํ ์ด์ ์ด๋ค.
๋ค์๊ณผ ๊ฐ์ด Hello
๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ API๋ฅผ ์์ฑํ์ฌ ํธ์ถํด๋ณด๋ฉด, json ํ์์ผ๋ก ๋ณํ๋์ด ๋ณด์ด๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
@GetMapping("hello-api")
@ResponseBody
public Hello helloApi(@RequestParam("name") String name) {
Hello hello = new Hello();
hello.setName(name);
return hello;
}
static class Hello {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@ResponseBody
์ด๋
ธํ
์ด์
์ ๋ถ์ด์ง ์์ผ๋ฉด ํ
ํ๋ฆฟ ํ์ผ ์์ฒด๋ฅผ ๋ฐํํ๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผํ๋ฏ๋ก, ์คํ ์ Template์ด ์๊ฑฐ๋ Template Resolver๊ฐ ์ ๊ทผํ ์ ์๋ค๋ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
Exception processing template "hello-api": Error resolving template [hello-api], template might not exist or might not be accessible by any of the configured Template Resolvers
๊ทธ๋ ๋ค๋ฉด json์ ๊ฐ์ฒด๋ก, ๊ฐ์ฒด๋ json์ผ๋ก ๋ณํํด์ฃผ๋ ๋ถ๋ถ์ ์ด๋์ผ๊น?
spring-web์ springframework/http/converter ํ์์ ์กด์ฌํ๋ ์ธํฐํ์ด์ค์ธ HttpMessageConverter
๋ HTTP request์ response๋ฅผ ๊ฐ๊ฐ ๊ฐ์ฒด์ ์๋ต ๋ฉ์์ง๋ก ๋ณํํด์ฃผ๋ ๋์์ ํ๋ค.
public interface HttpMessageConverter<T> {
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
List<MediaType> getSupportedMediaTypes();
default List<MediaType> getSupportedMediaTypes(Class<?> clazz) {
return (canRead(clazz, null) || canWrite(clazz, null) ?
getSupportedMediaTypes() : Collections.emptyList());
}
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
canRead
, canWrite
ํจ์์์ ์ฃผ์ด์ง ๊ฐ์ฒด๋ฅผ ์ปจ๋ฒํฐ๊ฐ ์ฝ๊ฑฐ๋ ์ธ ์ ์๋์ง ํ์ธํ๊ณ return๊ฐ์ด True
์ธ ๊ฒฝ์ฐ read
, write
์์ ๋ณํ ์์
์ ์คํํ๋ค.
HttpMessageConverter
์ธํฐํ์ด์ค๋ฅผ ํตํด ๊ตฌํ๋ ์ฌ๋ฌ Converter๋ค ์ค AbstractJackson2HttpMessageConverter
์์ ๋ณํ ๊ณผ์ ์ ์์ธํ ํ์ธํ ์ ์๋ค.
public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> {
...
}