๐ŸŒฑ Spring @RequestParam @ResponseBody

Spring์—์„œ Controller ์ž‘์„ฑ ์‹œ @RequestParam๊ณผ @ResponseBody ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๊ณค ํ•˜๋Š”๋ฐ, ๊ฐ ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ์™€ ๋™์ž‘ ๊ณผ์ •์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž.

@RequestParam

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;
}

hello ๊ฐœ๋ฏธ

@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());
		}
	}

@ResponseBody

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;
    }
}

hello ๊ฐœ๋ฏธ

@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> {
		...
}

Written by@ugaemi
Record things I want to remember

๐Ÿฑ GitHub๐Ÿ“š Reading Space