Skip to content

Latest commit

ย 

History

History
516 lines (393 loc) ยท 20.4 KB

Converter, Formatter.md

File metadata and controls

516 lines (393 loc) ยท 20.4 KB

๋ชจ๋ธ ๋ฐ”์ธ๋”ฉ ๋ฐ ๊ฒ€์ฆ

ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ์— @ModelAttribute ๊ฐ€ ์ง€์ •๋˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ผ์ด ์ผ์–ด๋‚œ๋‹ค.

  • ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž…์˜ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋งŒ๋“ ๋‹ค.
    • Ex. @ModelAttribute User user : User ๋ผ๋Š” ์ƒˆ๋กœ์šด ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋งŒ๋“ ๋‹ค.
    • @SessionAttributes ์— ์˜ํ•ด ์„ธ์…˜์— ์ €์žฅ๋œ ๋ชจ๋ธ ์˜ค๋ธŒ์ ํŠธ๊ฐ€ ์žˆ๋‹ค๋ฉด, ์„ธ์…˜์— ์ €์žฅ๋˜์–ด ์žˆ๋Š” ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.
  • ์›น ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์˜ค๋ธŒ์ ํŠธ ํ”„๋กœํผํ‹ฐ์— ๋ฐ”์ธ๋”ฉ ํ•œ๋‹ค.
    • HTTP ๋ฅผ ํ†ตํ•ด ์ „๋‹ฌ๋˜๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฌธ์ž์—ด ํ˜•์‹์ด๋ผ์„œ String ๊ฐ์ฒด๊ฐ€ ์•„๋‹Œ ์ด์ƒ ์ ์ ˆํ•œ ๋ณ€ํ™˜์ด ํ•„์š”ํ•˜๋‹ค.
    • ์ด๋•Œ, ์Šคํ”„๋ง์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋ณธ ํ”„๋กœํผํ‹ฐ ์—๋””ํ„ฐ๋ฅผ ํ†ตํ•ด์„œ HTTP Parameter -> Object Property ๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.
    • ๋ณ€ํ™˜์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฉด BindingResult ์•ˆ์— ์˜ค๋ฅ˜๋ฅผ ์ €์žฅํ•ด์„œ ์ ์ ˆํ•œ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์•ผ ํ•œ๋‹ค.
  • ๋ชจ๋ธ์˜ ๊ฐ’์„ ๊ฒ€์ฆํ•œ๋‹ค.
    • ํƒ€์ž…์— ๋Œ€ํ•œ ๊ฒ€์ฆ์€ ๋๋‚ฌ์ง€๋งŒ, ๊ทธ ์™ธ์˜ ๊ฒ€์ฆํ•  ๋‚ด์šฉ์ด ์žˆ๋‹ค๋ฉด ์ ์ ˆํ•œ ๊ฒ€์ฆ๊ธฐ๋ฅผ ๋“ฑ๋กํ•ด์„œ ๋ชจ๋ธ์˜ ๋‚ด์šฉ์„ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋‹ค.

์Šคํ”„๋ง์—์„œ ๋ฐ”์ธ๋”ฉ์ด๋ž€ ์˜ค๋ธŒ์ ํŠธ์˜ ํ”„๋กœํผํ‹ฐ์— ๊ฐ’์„ ๋„ฃ๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค.

PropertyEditor

  • ์Šคํ”„๋ง์ด ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๋ฐ”์ธ๋”ฉ์šฉ ํƒ€์ž… ๋ณ€ํ™˜ API ์ด๋‹ค.
  • PropertyEditor ๋Š” ์‚ฌ์‹ค ์Šคํ”„๋ง API ๋Š” ์•„๋‹ˆ๊ณ  ์ž๋ฐ”๋นˆ ํ‘œ์ค€์— ์ •์˜๋œ ์ธํ„ฐํŽ˜์ด์Šค๋‹ค.
  • XML ์˜ value ์• ํŠธ๋ฆฌ๋ทฐํŠธ๋ž‘, @Controller ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ์— ์ ์šฉ๋œ๋‹ค.
<bean id="dataSource" class="org...SimpleDriverDataSource">
 <!-- com.mysql.jdbc.Driver ํด๋ž˜์Šค ํƒ€์ž…์˜ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด์„œ driverClass ๋ผ๋Š” ํ”„๋กœํผํ‹ฐ์— ๋ฐ”์ธ๋”ฉํ•œ๋‹ค. -->
 <property name = "driverClass"  value = "com.mysql.jdbc.Driver" />
</bean>
// CharsetEditor ์‚ฌ์šฉ
@RequestMapping("/hello")
public void hello(@RequestParam Charset charset, Model model) {
}

PropertyEditor ์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ฉ”์„œ๋“œ

HTTP ์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ๊ฐ™์€ ๋ฌธ์ž์—ด์€ ์ŠคํŠธ๋ง ํƒ€์ž…์œผ๋กœ ์„œ๋ธ”๋ฆฟ์—์„œ ๊ฐ€์ ธ์˜จ๋‹ค.

  • String to Object
    • setAsText() ๋กœ String ํƒ€์ž…์˜ ๋ฌธ์ž์—ด์„ ๋„ฃ๊ณ  getValue() ๋กœ ๋ณ€ํ™˜๋œ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.
  • Object to String
    • setValue() ๋กœ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋„ฃ๊ณ  getAsText() ๋กœ ๋ณ€ํ™˜๋œ ๋ฌธ์ž์—ด์„ ๊ฐ€์ ธ์˜จ๋‹ค.

๋”ฐ๋ผ์„œ, ์ปค์Šคํ…€ ํ”„๋กœํผํ‹ฐ ์—๋””ํ„ฐ๋ฅผ ๋งŒ๋“ค ๋•Œ๋Š”, setAsText(), getAsText() ๋ถ€๋ถ„๋งŒ ์†๋ณด๋ฉด ๋œ๋‹ค.

์ปค์Šคํ…€ ํ”„๋กœํผํ‹ฐ ์—๋””ํ„ฐ

PropertyEditor ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๊ธฐ๋ณด๋‹ค๋Š” ๊ธฐ๋ณธ ๊ตฌํ˜„์ด ๋˜์–ด์žˆ๋Š” PropertyEditorSupport ํด๋ž˜์Šค๋ฅผ ์ƒ์†ํ•ด์„œ ํ•„์š”ํ•œ ๋ฉ”์„œ๋“œ๋งŒ ์˜ค๋ฒ„๋ผ์ด๋”ฉ ํ•˜๋Š”๊ฒŒ ๋‚ซ๋‹ค.

class LevelPropertyEditor: PropertyEditorSupport() {

    override fun getAsText(): String {
        // this.value -> setValue ์— ์˜ํ•ด ์ €์žฅ๋œ Level ํƒ€์ž…์˜ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๊ฐ€์ ธ์™€์„œ ๊ฐ’์„ ๋ฌธ์ž๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.
        return ((this.value as Level).intValue().toString())
    }

    override fun setAsText(text: String?) {
        this.value = text?.trim()?.let { Level.valueOf(it.toInt()) }
    }
}

๋งŒ๋“  ์ปค์Šคํ…€ ํ”„๋กœํผํ‹ฐ ์—๋””ํ„ฐ๊ฐ€ ์Šคํ”„๋ง MVC ์—์„œ ๋™์ž‘ํ•˜๊ฒŒ ํ•˜๋ ค๋ฉด @InitBinder ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

@InitBinder

  • ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ์—์„œ ๋ฐ”์ธ๋”ฉ์ด ์–ด๋–ป๊ฒŒ ์ผ์–ด๋‚ ๊นŒ?
    • @Controller ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์ค„ ์ฑ…์ž„์ด ์žˆ๋Š” AnnotationMethodHandlerAdapter ๋Š” @RequestParam, @ModelAttribute ๋“ฑ HTTP ์š”์ฒญ์„ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ณ€์ˆ˜์— ๋ฐ”์ธ๋”ฉ ํ•ด์ฃผ๋Š” ์ž‘์—…์ด ํ•„์š”ํ•œ ์–ด๋…ธํ…Œ์ด์…˜์„ ๋งŒ๋‚˜๋ฉด ๋จผ์ € WebDataBinder ๋ผ๋Š” ๊ฒƒ์„ ๋งŒ๋“ ๋‹ค.
  • WebDataBinder
    • HTTP ์š”์ฒญ์œผ๋กœ๋ถ€ํ„ฐ ๊ฐ€์ ธ์˜จ ๋ฌธ์ž์—ด์„ ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž…์˜ ์˜ค๋ธŒ์ ํŠธ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์ด ์žˆ๋‹ค. ์ด๋•Œ PropertyEditor ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
    • ๋”ฐ๋ผ์„œ, ์ปค์Šคํ…€ ํ”„๋กœํผํ‹ฐ ์—๋””ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” WebDataBinder ์— ๋“ฑ๋กํ•ด์ค˜์•ผ ํ•œ๋‹ค.
    • WebDataBinder ๋Š” ์ปค์Šคํ…€ ํ”„๋กœํผํ‹ฐ ์—๋””ํ„ฐ๊ฐ€ ์žˆ์œผ๋ฉด ๋จผ์ € ์ ์šฉํ•˜๊ณ , ์ ์ ˆํ•œ ํ”„๋กœํผํ‹ฐ ์—๋””ํ„ฐ๊ฐ€ ์—†๋‹ค๋ฉด ๊ทธ๋•Œ ์Šคํ”„๋ง์—์„œ ์ œ๊ณตํ•˜๋Š” ๋””ํดํŠธ ํ”„๋กœํผํ‹ฐ ์—๋””ํ„ฐ์ค‘ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค.
@RestController
class ConversionController {

    @InitBinder
    fun initBinder(webDataBinder: WebDataBinder) {
        webDataBinder.registerCustomEditor(Level::class.java, LevelPropertyEditor())
    }
    
    @GetMapping("/level")
    fun levelCustomEditor(@RequestParam level: Level): Int {
        return level.intValue()
    }
}

ํ”„๋กœํ† ํƒ€์ž… ๋นˆ ํ”„๋กœํผํ‹ฐ ์—๋””ํ„ฐ

์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋งค๋ฒˆ ์ƒˆ๋กœ ๋งŒ๋“œ๋Š” ๋Œ€์‹  ํ”„๋กœํผํ‹ฐ ์—๋””ํ„ฐ๋ฅผ ์‹ฑ๊ธ€ํ†ค ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•ด๋‘๊ณ  ๊ณต์œ ํ•ด์„œ ์“ธ ์ˆ˜ ์—†์„๊นŒ? ์•„์‰ฝ์ง€๋งŒ ํ”„๋กœํผํ‹ฐ ์—๋””ํ„ฐ๋Š” ์‹ฑ๊ธ€ํ†ค ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋  ์ˆ˜ ์—†๋‹ค. ํ”„๋กœํผํ‹ฐ ์—๋””ํ„ฐ์— ์˜ํ•ด ํƒ€์ž…์ด ๋ณ€๊ฒฝ๋˜๋Š” ์˜ค๋ธŒ์ ํŠธ๋Š” ํ•œ ๋ฒˆ์€ ํ”„๋กœํผํ‹ฐ ์—๋””ํ„ฐ ์˜ค๋ธŒ์ ํŠธ ๋‚ด๋ถ€์— ์ €์žฅ๋œ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ, ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๋งŒ๋“ค์–ด ๊ณต์œ ํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ๋œ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ํ”„๋กœํผํ‹ฐ ์—๋””ํ„ฐ๊ฐ€ ๋‹ค๋ฅธ ์Šคํ”„๋ง ๋นˆ์„ ์ฐธ์กฐํ•ด์•ผ ํ•œ๋‹ค๋ฉด ์–ด๋–จ๊นŒ?

๋‹ค๋ฅธ ๋นˆ์„ ์ฐธ์กฐํ•ด์„œ DI ๋ฐ›์œผ๋ ค๋ฉด ์ž์‹ ๋„ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋˜์–ด์•ผ ํ•œ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ํ”„๋กœํผํ‹ฐ ์—๋””ํ„ฐ๊ฐ€ ๋‹ค๋ฅธ ๋นˆ์„ DI ๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก ์ž์‹ ๋„ ๋นˆ์œผ๋กœ์„œ ๋“ฑ๋ก๋˜๋ฉด์„œ ๋™์‹œ์— ๋งค๋ฒˆ ์ƒˆ๋กœ์šด ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด์„œ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ ค๋ฉด ํ”„๋กœํ† ํƒ€์ž… ์Šค์ฝ”ํ”„์˜ ๋นˆ์œผ๋กœ ๋งŒ๋“ค์–ด์ ธ์•ผ ํ•œ๋‹ค.

ํ”„๋กœํ† ํƒ€์ž… ์Šค์ฝ”ํ”„ ๋นˆ์€ ๋งค๋ฒˆ ๋นˆ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์š”์ฒญํ•ด์„œ ์ƒˆ๋กœ์šด ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์œผ๋ฉด์„œ DI ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

@Component
@Scope("prototype")
public class CodePropertyEditor extends PropertyEditorSupport{
  @Atuworied codeService;

  @Override
	public void setAsText(String text) throws IllegalArgumentException {
		Code code = codeService.getCode(Integer.valueOf(text));

		this.setValue(code);
	}
}
@Controller
public class UserController{
  @Inject Provider<CodePropertyEditor> codePropertyEditorProvider;

  @InitBinder
  public void initBinder(WebDataBinder dataBinder){
    dataBinder.registerCustomEditor(Code.class, codePropertyEditorProvider.get());
  }

  @RequestMapping("/user", method=RequestMethod.POST)
  public String userAdd(@ModelAttribute User user){
    // ...
  }
}
  • ์ด ๋ฐฉ์‹์˜ ์žฅ์ ์€ ํ•ญ์ƒ ์™„์ „ํ•œ ๋„๋ฉ”์ธ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋ฆฌํ„ดํ•ด์ฃผ๋ฏ€๋กœ, ์•ž์„œ ์ œ๊ธฐํ–ˆ๋˜ ์œ„ํ—˜์ด ์—†์–ด์ง„๋‹ค.
  • ๋‹จ์ ์œผ๋กœ๋Š” ๋งค๋ฒˆ DB์—์„œ ์กฐํšŒ๋ฅผ ํ•ด์•ผํ•˜๋ฏ€๋กœ ์„ฑ๋Šฅ์— ์กฐ๊ธˆ ๋ถ€๋‹ด์„ ์ฃผ๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.
  • JPA ์™€ ๊ฐ™์ด ์—”ํ‹ฐํ‹ฐ ๋‹จ์œ„์˜ ์บ์‹ฑ ๊ธฐ๋ฒ•์ด ๋ฐœ๋‹ฌํ•œ ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, DB์—์„œ ์กฐํšŒํ•˜๋Š” ๋Œ€์‹  ๋ฉ”๋ชจ๋ฆฌ์—์„œ ๋ฐ”๋กœ ์ฝ์–ด์˜ฌ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ DB ๋ถ€ํ•˜์— ๋Œ€ํ•œ ๊ฑฑ์ •์€ ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

Converter

PropertyEditor๋Š” ๊ทผ๋ณธ์ ์ธ ๋‹จ์ ์ด ์žˆ๋‹ค. ์ƒํƒœ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฏ€๋กœ ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๋“ฑ๋กํ•  ์ˆ˜ ์—†๊ณ , ํ•ญ์ƒ ์ƒˆ๋กœ์šด ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค๋Š” ์ ์ด๋‹ค. ๋ฌผ๋ก  ์ƒ์„ฑ๋˜๋Š” ์˜ค๋ธŒ์ ํŠธ ์ž์ฒด๊ฐ€ ๊ฐ€๋ณ๊ธฐ ๋•Œ๋ฌธ์— ํฌ๊ฒŒ ๋ฌธ์ œ๋  ๊ฒƒ์€ ์—†์ง€๋งŒ, ์‹ฑ๊ธ€ํ†ค ์„œ๋น„์Šค ์˜ค๋ธŒ์ ํŠธ ์ค‘์‹ฌ์˜ ์Šคํ”„๋ง ๊ณผ๋Š” ์ž˜ ์–ด์šธ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค. ํŠนํžˆ, ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•ด์„œ ์‚ฌ์šฉํ•  ๋–„๋Š” ๋ฐ˜๋“œ์‹œ ํ”„๋กœํ† ํƒ€์ž… ์Šค์ฝ”ํ”„๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ถˆํŽธํ•˜๋‹ค.

์Šคํ”„๋ง 3.0์ดํ›„๋กœ ์ด๋Ÿฌํ•œ PropertyEditor ์˜ ๋‹จ์ ์„ ๋ณด์™„ํ•ด์ฃผ๋Š” Converter ๋ผ๋Š” ํƒ€์ž… ๋ณ€ํ™˜ API ๊ฐ€ ๋“ฑ์žฅํ•˜์˜€๋‹ค. Converter ๋Š” PropertyEditor ์™€ ๋‹ฌ๋ฆฌ ๋ณ€ํ™˜๊ณผ์ •์—์„œ ๋ฉ”์„œ๋“œ๊ฐ€ ํ•œ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋œ๋‹ค. ์ฆ‰, ์ƒํƒœ๋ฅผ ๊ฐ€์ง€์ง€ ์•Š๋Š”๋‹ค๋Š” ๋œป์ด๊ณ , ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋œป์ด๋‹ค.

Converter

  • Converter ์ธํ„ฐํŽ˜์ด์Šค
public interface Converter<S, T>{
  T convert(s source);
}
class LevelToStringConverter: Converter<Level, String> {
    override fun convert(source: Level): String {
        return source.intValue().toString()
    }
}

class StringToLevelConverter: Converter<String, Level> {
    override fun convert(source: String): Level {
        return Level.valueOf(source.toInt())
    }
}

์ด๋ ‡๊ฒŒ ๋‘ ๊ฐœ์˜ ์ปจ๋ฒ„ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด PropertyEditor ๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ ์ฒ˜๋Ÿผ ๋™์ผํ•œ ํšจ๊ณผ๋ฅผ ๋‚ผ ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ Thread-safe ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ๋„ ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

ConversionService

  • ConversionService ๋Š” ์—ฌ๋Ÿฌ ์ข…๋ฅ˜์˜ ์ปจ๋ฒ„ํ„ฐ๋ฅผ ์ด์šฉํ•ด์„œ ํ•˜๋‚˜ ์ด์ƒ์˜ ํƒ€์ž… ๋ณ€ํ™˜ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•ด์ฃผ๋Š” ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋งŒ๋“ค ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋‹ค.
  • ๋ณดํ†ต ConversionService ๋ฅผ ๊ตฌํ˜„ํ•œ GenericConversionService ํด๋ž˜์Šค๋ฅผ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
  • GenericConversionService ๋Š” ์Šคํ”„๋ง์˜ ๋‹ค์–‘ํ•œ ํƒ€์ž… ๋ณ€ํ™˜ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋Š” ConverterRegistry ์ธํ„ฐํŽ˜์ด์Šค๋„ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋‹ค.
  • ์ƒˆ๋กœ์šด ํƒ€์ž… ๋ณ€ํ™˜ ์˜ค๋ธŒ์ ํŠธ๋Š GenericConverter, ConverterFactory ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ๋‹ค.
  • GenericConversionService ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๊ณ  ํ•„์š”ํ•œ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ DI ๋ฐ›์•„์„œ @InitBinder ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด WebDataBinder ์— ์„ค์ •ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ปจํŠธ๋กค๋Ÿฌ์˜ ๋ฐ”์ธ๋”ฉ ์ž‘์—…์— ์ปจ๋ฒ„ํ„ฐ๋ฅผ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ConversionService ํƒ€์ž…์˜ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ํ†ตํ•ด WebDataBinder ์— ์„ค์ •ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

์Šคํ”„๋ง XML ์„ค์ •

<bean class="org.springframework..ConversionServiceFactoryBean">
  <property name="converters">
    <set>
      <bean class="LevelConverter" />
      <!-- ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์€ Converter๋“ค... -->
    </set>
  </property>
</bean>
@Controller
public class UserController{
  @Autowired ConversionService conversionService;

  @InitBinder
  public void initBinder(WebDataBinder dataBinder){
    dataBinder.setConversionService(this.conversionService);
  }

  @RequestMapping("/user", method=RequestMethod.GET)
  public String userSearch(@RequestParam Level level){
    // ...
  }
}

์Šคํ”„๋ง ๋ถ€ํŠธ ์„ค์ •

์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. @InitBinder ๋ฅผ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ์ปจ๋ฒ„ํ„ฐ๊ฐ€ ๋™์ž‘ํ•œ๋‹ค.

@Configuration
class WebMvcConfig: WebMvcConfigurer {

    override fun addFormatters(registry: FormatterRegistry) {
        registry.addConverter(StringToLevelConverter())
        registry.addConverter(LevelToStringConverter())
    }
}

Formatter

  • ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๊ฐ์ฒด๋ฅผ ๋ฌธ์ž๋กœ, ๋ฌธ์ž๋ฅผ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์˜ˆ
    • ํ™”๋ฉด์— ์ˆซ์ž๋ฅผ ์ถœ๋ ฅํ•ด์•ผ ํ•˜๋Š”๋ฐ, Integer String ์ถœ๋ ฅ ์‹œ์ ์— ์ˆซ์ž 1000 ๋ฌธ์ž "1,000" ์ด๋ ‡๊ฒŒ 1000 ๋‹จ์œ„์— ์‰ผํ‘œ๋ฅผ ๋„ฃ์–ด์„œ ์ถœ๋ ฅํ•˜๊ฑฐ๋‚˜, ๋˜๋Š” "1,000" ๋ผ๋Š” ๋ฌธ์ž๋ฅผ 1000 ์ด๋ผ๋Š” ์ˆซ์ž๋กœ ๋ณ€๊ฒฝํ•ด์•ผ ํ•œ๋‹ค.
    • ๋‚ ์งœ ๊ฐ์ฒด๋ฅผ ๋ฌธ์ž์ธ "2021-01-01 10:50:11" ์™€ ๊ฐ™์ด ์ถœ๋ ฅํ•˜๊ฑฐ๋‚˜ ๋˜๋Š” ๊ทธ ๋ฐ˜๋Œ€์˜ ์ƒํ™ฉ
    • ์—ฌ๊ธฐ์— ์ถ”๊ฐ€๋กœ ๋‚ ์งœ ์ˆซ์ž์˜ ํ‘œํ˜„ ๋ฐฉ๋ฒ•์€ Locale ํ˜„์ง€ํ™” ์ •๋ณด๊ฐ€ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๋‹ค.

์ด๋ ‡๊ฒŒ ๊ฐ์ฒด๋ฅผ ํŠน์ •ํ•œ ํฌ๋งท์— ๋งž์ถ”์–ด ๋ฌธ์ž๋กœ ์ถœ๋ ฅํ•˜๊ฑฐ๋‚˜ ๋˜๋Š” ๊ทธ ๋ฐ˜๋Œ€์˜ ์—ญํ• ์„ ํ•˜๋Š” ๊ฒƒ์— ํŠนํ™”๋œ ๊ธฐ๋Šฅ์ด ๋ฐ”๋กœ ํฌ๋งทํ„ฐ(Formatter)์ด๋‹ค. ํฌ๋งทํ„ฐ๋Š” ์ปจ๋ฒ„์ „์˜ ํŠน๋ณ„ํ•œ ๋ฒ„์ „์œผ๋กœ ์ดํ•ดํ•˜๋ฉด ๋œ๋‹ค.

  • Converter vs Formatter
    • Converter ๋Š” ๋ฒ”์šฉ(๊ฐ์ฒด -> ๊ฐ์ฒด)
    • Formatter ๋Š” ๋ฌธ์ž์— ํŠนํ™”(๊ฐ์ฒด -> ๋ฌธ์ž, ๋ฌธ์ž -> ๊ฐ์ฒด) + ํ˜„์ง€ํ™”(Locale)
    • Converter ์˜ ํŠน๋ณ„ํ•œ ๋ฒ„์ „

ํฌ๋งทํ„ฐ - Formatter ๋งŒ๋“ค๊ธฐ

ํฌ๋งทํ„ฐ(Formatter)๋Š” ๊ฐ์ฒด๋ฅผ ๋ฌธ์ž๋กœ ๋ณ€๊ฒฝํ•˜๊ณ , ๋ฌธ์ž๋ฅผ ๊ฐ์ฒด๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๋‘ ๊ฐ€์ง€ ๊ธฐ๋Šฅ์„ ๋ชจ๋‘ ์ˆ˜ํ–‰ํ•œ๋‹ค.

  • String print(T object, Locale locale) : ๊ฐ์ฒด๋ฅผ ๋ฌธ์ž๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค.
  • T parse(String text, Locale locale) : ๋ฌธ์ž๋ฅผ ๊ฐ์ฒด๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค.
/**
 * Formats objects of type T.
 * A Formatter is both a Printer <i>and</i> a Parser for an object type.
 *
 * @author Keith Donald
 * @since 3.0
 * @param <T> the type of object this Formatter formats
 */
public interface Formatter<T> extends Printer<T>, Parser<T> {

}
public interface Printer<T> {
  String print(T object, Locale locale);
}

public interface Parser<T> {
  T parse(String text, Locale locale) throws ParseException;
}

์ˆซ์ž 1000 ์„ ๋ฌธ์ž "1,000" ์œผ๋กœ ๊ทธ๋Ÿฌ๋‹ˆ๊นŒ, 1000 ๋‹จ์œ„๋กœ ์‰ผํ‘œ๊ฐ€ ๋“ค์–ด๊ฐ€๋Š” ํฌ๋งท์„ ์ ์šฉํ•ด๋ณด์ž. ๊ทธ๋ฆฌ๊ณ  ๊ทธ ๋ฐ˜๋Œ€๋„ ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š” ํฌ๋งทํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž.

@Slf4j
public class MyNumberFormatter implements Formatter<Number> {

    // ๋ฌธ์ž๋ฅผ ์ˆซ์ž๋กœ
    @Override
    public Number parse(String text, Locale locale) throws ParseException {
        log.info("text={}, locale={}", text, locale);
        // "1,000" -> 1000
        NumberFormat format = NumberFormat.getInstance(locale);
        return format.parse(text);
    }

    // ์ˆซ์ž๋ฅผ ๋ฌธ์ž๋กœ
    @Override
    public String print(Number object, Locale locale) {
        log.info("object={}, locale={}", object, locale);
        return NumberFormat.getInstance(locale).format(object);
    }
}

"1,000" ์ฒ˜๋Ÿผ ์ˆซ์ž ์ค‘๊ฐ„์˜ ์‰ผํ‘œ๋ฅผ ์ ์šฉํ•˜๋ ค๋ฉด ์ž๋ฐ”๊ฐ€ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” NumberFormat ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. ์ด ๊ฐ์ฒด๋Š” Locale ์ •๋ณด๋ฅผ ํ™œ์šฉํ•ด์„œ ๋‚˜๋ผ๋ณ„๋กœ ๋‹ค๋ฅธ ์ˆซ์ž ํฌ๋งท์„ ๋งŒ๋“ค์–ด์ค€๋‹ค.

  • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ
class MyNumberFormatterTest {

    MyNumberFormatter formatter = new MyNumberFormatter();

    @Test
    void parse() throws ParseException {
        Number result = formatter.parse("1,000", Locale.KOREA);
        assertThat(result).isEqualTo(1000L); // Long ํƒ€์ž… ์ฃผ์˜
    }

    @Test
    void print() {
        String result = formatter.print(1000, Locale.KOREA);
        assertThat(result).isEqualTo("1,000");
    }
}

parse() ์˜ ๊ฒฐ๊ณผ๊ฐ€ Long ์ด๊ธฐ ๋•Œ๋ฌธ์— isEqualTo(1000L) ์„ ํ†ตํ•ด ๋น„๊ตํ•  ๋•Œ ๋งˆ์ง€๋ง‰์— L ์„ ๋„ฃ์–ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

  • ์‹คํ–‰ ๊ฒฐ๊ณผ
MyNumberFormatter - text=1,000, locale=ko_KR
MyNumberFormatter - object=1000, locale=ko_KR

์Šคํ”„๋ง์€ ์šฉ๋„์— ๋”ฐ๋ผ ๋‹ค์–‘ํ•œ ๋ฐฉ์‹์˜ ํฌ๋งทํ„ฐ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

  • Formatter ํฌ๋งทํ„ฐ
  • AnnotationFormatterFactory ํ•„๋“œ์˜ ํƒ€์ž…์ด๋‚˜ ์• ๋…ธํ…Œ์ด์…˜ ์ •๋ณด๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํฌ๋งทํ„ฐ

์ž์„ธํ•œ ๋‚ด์šฉ์€ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์ž.

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#format

ํฌ๋งทํ„ฐ๋ฅผ ์ง€์›ํ•˜๋Š” ์ปจ๋ฒ„์ „ ์„œ๋น„์Šค

์ปจ๋ฒ„์ „ ์„œ๋น„์Šค์—๋Š” ์ปจ๋ฒ„ํ„ฐ๋งŒ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๊ณ , ํฌ๋งทํ„ฐ๋ฅผ ๋“ฑ๋กํ•  ์ˆ˜ ๋Š” ์—†๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ƒ๊ฐํ•ด๋ณด๋ฉด ํฌ๋งทํ„ฐ๋Š” ๊ฐ์ฒด -> ๋ฌธ์ž, ๋ฌธ์ž -> ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ํŠน๋ณ„ํ•œ ์ปจ๋ฒ„ํ„ฐ์ผ ๋ฟ์ด๋‹ค. ํฌ๋งทํ„ฐ๋ฅผ ์ง€์›ํ•˜๋Š” ์ปจ๋ฒ„์ „ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ปจ๋ฒ„์ „ ์„œ๋น„์Šค์— ํฌ๋งทํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‚ด๋ถ€์—์„œ ์–ด๋Œ‘ํ„ฐ ํŒจํ„ด์„ ์‚ฌ์šฉํ•ด์„œ Formatter ๊ฐ€ Converter ์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜๋„๋ก ์ง€์›ํ•œ๋‹ค.

FormattingConversionService ๋Š” ํฌ๋งทํ„ฐ๋ฅผ ์ง€์›ํ•˜๋Š” ์ปจ๋ฒ„์ „ ์„œ๋น„์Šค์ด๋‹ค.

DefaultFormattingConversionService ๋Š” FormattingConversionService ์— ๊ธฐ๋ณธ์ ์ธ ํ†ตํ™”, ์ˆซ์ž ๊ด€๋ จ ๋ช‡๊ฐ€์ง€ ๊ธฐ๋ณธ ํฌ๋งทํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ ์ œ๊ณตํ•œ๋‹ค.

  • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ
public class FormattingConversionServiceTest {

    @Test
    void formattingConversionService() {
        DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
        
        // ์ปจ๋ฒ„ํ„ฐ ๋“ฑ๋ก
        conversionService.addConverter(new StringToIpPortConverter());
        conversionService.addConverter(new IpPortToStringConverter());
        
        // ํฌ๋งทํ„ฐ ๋“ฑ๋ก
        conversionService.addFormatter(new MyNumberFormatter());

        // ์ปจ๋ฒ„ํ„ฐ ์‚ฌ์šฉ
        IpPort ipPort = conversionService.convert("127.0.0.1:8080", IpPort.class);
        assertThat(ipPort).isEqualTo(new IpPort("127.0.0.1", 8080));
        
        // ํฌ๋งทํ„ฐ ์‚ฌ์šฉ
        assertThat(conversionService.convert(1000, String.class)).isEqualTo("1,000");
        assertThat(conversionService.convert("1,000", Long.class)).isEqualTo(1000L);

    }
}

DefaultFormattingConversionService ์ƒ์† ๊ด€๊ณ„

FormattingConversionService ๋Š” ConversionService ๊ด€๋ จ ๊ธฐ๋Šฅ์„ ์ƒ์†๋ฐ›๊ธฐ ๋•Œ๋ฌธ์— ๊ฒฐ๊ณผ์ ์œผ๋กœ ์ปจ๋ฒ„ํ„ฐ๋„ ํฌ๋งทํ„ฐ๋„ ๋ชจ๋‘ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์‚ฌ์šฉํ•  ๋•Œ๋Š” ConversionService ๊ฐ€ ์ œ๊ณตํ•˜๋Š” convert ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

์ถ”๊ฐ€๋กœ ์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” DefaultFormattingConversionService ๋ฅผ ์ƒ์† ๋ฐ›์€ WebConversionService ๋ฅผ ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉํ•œ๋‹ค.

ํฌ๋งทํ„ฐ ์ ์šฉํ•˜๊ธฐ

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // ์ฃผ์„์ฒ˜๋ฆฌ ์šฐ์„ ์ˆœ์œ„
//        registry.addConverter(new StringToIntegerConverter());
//        registry.addConverter(new IntegerToStringConverter());
        registry.addConverter(new StringToIpPortConverter());
        registry.addConverter(new IpPortToStringConverter());

        // ์ถ”๊ฐ€
        registry.addFormatter(new MyNumberFormatter());
    }
}

MyNumberFormatter ๋„ ์ˆซ์ž -> ๋ฌธ์ž, ๋ฌธ์ž -> ์ˆซ์ž๋กœ ๋ณ€๊ฒฝํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‘˜์˜ ๊ธฐ๋Šฅ์ด ๊ฒน์นœ๋‹ค. ์šฐ์„ ์ˆœ์œ„๋Š” ์ปจ๋ฒ„ํ„ฐ๊ฐ€ ์šฐ์„ ํ•˜๋ฏ€๋กœ ํฌ๋งทํ„ฐ๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š๊ณ , ์ปจ๋ฒ„ํ„ฐ๊ฐ€ ์ ์šฉ๋œ๋‹ค.

์Šคํ”„๋ง์ด ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋ณธ ํฌ๋งทํ„ฐ

์Šคํ”„๋ง์€ ์ž๋ฐ”์—์„œ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ํƒ€์ž…๋“ค์— ๋Œ€ํ•ด ์ˆ˜ ๋งŽ์€ ํฌ๋งทํ„ฐ๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•œ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ํฌ๋งทํ„ฐ๋Š” ๊ธฐ๋ณธ ํ˜•์‹์ด ์ง€์ •๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐ์ฒด์˜ ๊ฐ ํ•„๋“œ๋งˆ๋‹ค ๋‹ค๋ฅธ ํ˜•์‹์œผ๋กœ ํฌ๋งท์„ ์ง€์ •ํ•˜๊ธฐ๋Š” ์–ด๋ ต๋‹ค.

์Šคํ”„๋ง์€ ์ด๋Ÿฐ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์• ๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜์œผ๋กœ ์›ํ•˜๋Š” ํ˜•์‹์„ ์ง€์ •ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋งค์šฐ ์œ ์šฉํ•œ ํฌ๋งทํ„ฐ ๋‘ ๊ฐ€์ง€๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•œ๋‹ค.

  • @NumberFormat : ์ˆซ์ž ๊ด€๋ จ ํ˜•์‹ ์ง€์ • ํฌ๋งทํ„ฐ ์‚ฌ์šฉ, NumberFormatAnnotationFormatterFactory

  • @DateTimeFormat : ๋‚ ์งœ ๊ด€๋ จ ํ˜•์‹ ์ง€์ • ํฌ๋งทํ„ฐ ์‚ฌ์šฉ, Jsr310DateTimeFormatAnnotationFormatterFactory

  • FormatterController

@Controller
public class FormatterController {

    @GetMapping("/formatter/edit")
    public String formatterForm(Model model) {
        Form form = new Form();
        form.setNumber(10000);
        form.setLocalDateTime(LocalDateTime.now());
        model.addAttribute("form", form);
        return "formatter-form";
    }

    @PostMapping("/formatter/edit")
    public String formatterEdit(@ModelAttribute Form form) {
        return "formatter-view";
    }

    @Data
    static class Form {
        @NumberFormat(pattern = "###,###")
        private Integer number;

        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        private LocalDateTime localDateTime;
    }
}
  • formatter-from.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
 <meta charset="UTF-8">
 <title>Title</title>
</head>
<body>
<form th:object="${form}" th:method="post">
 number <input type="text" th:field="*{number}"><br/>
 localDateTime <input type="text" th:field="*{localDateTime}"><br/>
 <input type="submit"/>
</form>
</body>
</html>
  • formatter-view.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
 <meta charset="UTF-8">
 <title>Title</title>
</head>
<body>
<ul>
 <li>${form.number}: <span th:text="${form.number}" ></span></li>
 <li>${{form.number}}: <span th:text="${{form.number}}" ></span></li>
 <li>${form.localDateTime}: <span th:text="${form.localDateTime}" ></span></li>
 <li>${{form.localDateTime}}: <span th:text="${{form.localDateTime}}" ></span></li>
</ul>
</body>
</html>
  • ๊ฒฐ๊ณผ
${form.number}: 10000
${{form.number}}: 10,000
${form.localDateTime}: 2021-01-01T00:00:00
${{form.localDateTime}}: 2021-01-01 00:00:00

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#format-CustomFormatAnnotations

์ฃผ์˜

๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ(HttpMessageConverter)์—๋Š” ์ปจ๋ฒ„์ „ ์„œ๋น„์Šค๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค.

ํŠนํžˆ ๊ฐ์ฒด๋ฅผ JSON์œผ๋กœ ๋ณ€ํ™˜ํ•  ๋•Œ ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ์ด ๋ถ€๋ถ„์„ ๋งŽ์ด ์˜คํ•ดํ•˜๋Š”๋ฐ, HttpMessageConverter ์˜ ์—ญํ• ์€ HTTP ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์˜ ๋‚ด์šฉ์„ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•˜๊ฑฐ๋‚˜ ๊ฐ์ฒด๋ฅผ HTTP ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์— ์ž…๋ ฅํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด์„œ JSON์„ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๋Š” ๋‚ด๋ถ€์—์„œ Jackson ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ๊ฐ์ฒด๋ฅผ JSON์œผ๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค๋ฉด ๊ทธ ๊ฒฐ๊ณผ๋Š” ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋‹ฌ๋ฆฐ ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ JSON ๊ฒฐ๊ณผ๋กœ ๋งŒ๋“ค์–ด์ง€๋Š” ์ˆซ์ž๋‚˜ ๋‚ ์งœ ํฌ๋งท์„ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ์œผ๋ฉด ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์„ค์ •์„ ํ†ตํ•ด์„œ ํฌ๋งท์„ ์ง€์ •ํ•ด์•ผ ํ•œ๋‹ค.

๊ฒฐ๊ณผ์ ์œผ๋กœ ์ด๊ฒƒ์€ ์ปจ๋ฒ„์ „ ์„œ๋น„์Šค์™€ ์ „ํ˜€ ๊ด€๊ณ„๊ฐ€ ์—†๋‹ค. ์ปจ๋ฒ„์ „ ์„œ๋น„์Šค๋Š” @RequestParam , @ModelAttribute , @PathVariable , ๋ทฐ ํ…œํ”Œ๋ฆฟ ๋“ฑ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

References

  • ํ† ๋น„์˜ ์Šคํ”„๋ง3