์นดํ…Œ๊ณ ๋ฆฌ ์—†์Œ

[JPA] ๊ธ€ ์ƒ์„ธ๋ณด๊ธฐ(์กฐํšŒ) API ๊ตฌํ˜„ - 7

๋ฏธ๋กœ910 2024. 10. 2. 17:30
๐Ÿ’ก
1. ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ์— ๋Œ€ํ•œ ๊ฐœ๋…์„ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค.
2. ๋”ํ‹ฐ ์ฒดํ‚น ๊ฐœ๋…๊ณผ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ํŠน์ง•์„ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

Article ํด๋ž˜์Šค(์—”ํ‹ฐํ‹ฐ) ์ฝ”๋“œ ์ถ”๊ฐ€ ํ•˜๊ธฐ - 1
// ๋ฐ˜๋“œ์‹œ ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๊ฐ€ ์žˆ์–ด์•ผ ๋œ๋‹ค.
@Entity(name = "tb_article")
@NoArgsConstructor // ๊ธฐ๋ณธ ์ƒ์„ฑ์ž
@Data
public class Article {
	
	// ํŠน์ • ์ƒ์„ฑ์ž์—๋งŒ ๋นŒ๋” ํŒจํ„ด์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.
	@Builder
	public Article(String title, String content) {
		this.title = title;
		this.content = content;
	}
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY) //db๋กœ ์œ„์ž„
	@Column(name = "id", updatable = false)
	private Long id;
	
	@Column(name = "title", nullable = false) // not null 
	private String title;
	
	@Column(name = "content", nullable = false) // not null 
	private String content;
	
	// ๊ฐ์ฒด์˜ ์ƒํƒœ ๊ฐ’ ์ˆ˜์ •
	public void update(String title, String content) {
		// ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋ฐ˜๋“œ์‹œ ํ•ด์•ผ ํ•จ
		// ์ฆ‰, ๋ฐ์ดํ„ฐ๊ฐ€ ์—”ํ‹ฐํ‹ฐ์— ์ €์žฅ๋˜๊ธฐ ์ „์— ๋ฐ˜๋“œ์‹œ ๊ฒ€์ฆ
		
		if(title == null || title.trim().isEmpty()) {
			throw new Exception400("์ œ๋ชฉ์€ null ์ด๊ฑฐ๋‚˜ ๋นˆ ๋ฌธ์ž์—ด์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.");
		}
		
		if(content == null || content.trim().isEmpty()) {
			throw new Exception400("๋‚ด์šฉ์€ null ์ด๊ฑฐ๋‚˜ ๋นˆ ๋ฌธ์ž์—ด์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.");
		}
		
		this.title = title;
		this.content = content;
	}
	

}

๋„๋ฉ”์ธ ๋ชจ๋ธ - ํ˜„์‹ค ์„ธ๊ณ„์˜ ์ค‘์š”ํ•œ ๊ฐœ๋…์„ ์ฝ”๋“œ๋กœ ๋‚˜ํƒ€๋‚ธ ๊ฒƒ (๊ฒŒ์‹œ๊ธ€, ์‚ฌ์šฉ์ž, ๋Œ“๊ธ€, ์ฃผ๋ฌธ, ์ƒํ’ˆ)

๊ฐ์ฒด ์Šค์Šค๋กœ ์ž์‹ ์˜ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋„๋ก ํ•œ๋‹ค - ์ž์‹ ์˜ ๋ฐ์ดํ„ฐ์™€ ํ–‰๋™์— ์ฑ…์ž„์„ ์ง„๋‹ค.

 

application-dev.yml ์ˆ˜์ •
server:
  servlet:
    encoding:
      charset: utf-8         # ์š”์ฒญ ๋ฐ ์‘๋‹ต์— UTF-8 ์ธ์ฝ”๋”ฉ์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•œ๊ธ€ ๋ฐ ํŠน์ˆ˜๋ฌธ์ž๊ฐ€ ๊นจ์ง€์ง€ ์•Š๋„๋ก ์„ค์ •
      force: true            # ๊ฐ•์ œ๋กœ UTF-8 ์ธ์ฝ”๋”ฉ์„ ์ ์šฉ, ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋‹ค๋ฅธ ์ธ์ฝ”๋”ฉ์„ ์š”์ฒญํ•˜๋”๋ผ๋„ ๋ฌด์‹œํ•˜๊ณ  UTF-8์„ ์‚ฌ์šฉ
  port: 8080                 # ์„œ๋ฒ„๊ฐ€ 8080 ํฌํŠธ์—์„œ ์‹คํ–‰๋˜๋„๋ก ์„ค์ •

spring:
  mustache:
    servlet:
      expose-session-attributes: true  # Mustache ํ…œํ”Œ๋ฆฟ์—์„œ ์„ธ์…˜ ์†์„ฑ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉ
      expose-request-attributes: true  # Mustache ํ…œํ”Œ๋ฆฟ์—์„œ ์š”์ฒญ ์†์„ฑ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉ
  datasource:
    url: jdbc:mysql://localhost:3306/jpa_demo?useSSL=false&serverTimezone=Asia/Seoul&useLegacyDatetimeCode=false
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: asd123
                          # ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ธฐ๋ณธ ๋น„๋ฐ€๋ฒˆํ˜ธ (๋น„์–ด ์žˆ์Œ)
  h2:
    console:
      enabled: true   # H2 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฝ˜์†”์„ ํ™œ์„ฑํ™”ํ•˜์—ฌ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ
  #sql:
    #init:
      #data-locations:
        #- classpath:db/data.sql  # ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ดˆ๊ธฐํ™” ์‹œ ์‹คํ–‰ํ•  ๋ฐ์ดํ„ฐ ์‚ฝ์ž… SQL ํŒŒ์ผ์˜ ๊ฒฝ๋กœ (data.sql)
  jpa:
    hibernate:
      ddl-auto: update            # ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹œ์ž‘๋  ๋•Œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑ
    show-sql: true                # Hibernate๊ฐ€ ์‹คํ–‰ํ•˜๋Š” SQL ์ฟผ๋ฆฌ๋ฅผ ์ฝ˜์†”์— ์ถœ๋ ฅ
    properties:
      hibernate:
        format_sql: true          # ์ถœ๋ ฅ๋˜๋Š” SQL ์ฟผ๋ฆฌ๋ฅผ ํฌ๋งทํŒ…ํ•˜์—ฌ ์ฝ๊ธฐ ์‰ฝ๊ฒŒ ์ถœ๋ ฅ
    defer-datasource-initialization: true  # ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ดˆ๊ธฐํ™”๊ฐ€ ์ง€์—ฐ๋˜๋„๋ก ์„ค์ •ํ•˜์—ฌ JPA ์„ค์ • ํ›„์— ๋ฐ์ดํ„ฐ ์ดˆ๊ธฐํ™”

  output:
    ansi:
      enabled: always  # ์ฝ˜์†” ์ถœ๋ ฅ ์‹œ ANSI ์ƒ‰์ƒ์„ ํ•ญ์ƒ ์‚ฌ์šฉํ•˜๋„๋ก ์„ค์ • (์ƒ‰์ƒ์„ ํ†ตํ•ด ๋กœ๊ทธ๋ฅผ ๋” ์‰ฝ๊ฒŒ ๊ตฌ๋ถ„ ๊ฐ€๋Šฅ)

logging:
  level:
    '[com.example.class_blog_jpa_v1]': DEBUG  # ํŠน์ • ํŒจํ‚ค์ง€(com.tenco.blog_jpa_step1) ์ˆ˜์ค€์—์„œ DEBUG ๋ ˆ๋ฒจ๋กœ ๋กœ๊น…์„ ์„ค์ •

 

BlogService ํด๋ž˜์Šค์— ์ˆ˜์ • ๊ธฐ๋Šฅ๊ณผ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ - 2

์ˆ˜์ •๊ธฐ๋Šฅ์— @Transactional ์ฒ˜๋ฆฌ ํ•˜๊ธฐ
JpaRepository ๋ฉ”์„œ๋“œ์ธ save()๋‚˜ delete()๋ฅผ ์ง์ ‘์‚ฌ์šฉ ํ–ˆ์—ˆ์Œ. ์ด ๋ฉ”์„œ๋“œ๋“ค์€ ์ด๋ฏธ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์„œ๋น„์Šค ๊ณ„์ธต์—์„œ ์ถ”๊ฐ€๋กœ ํŠธ๋žœ์žญ์…˜์„ ์„ ์–ธํ•  ํ•„์š”๊ฐ€ ์—†์—ˆ์Œ.
@RequiredArgsConstructor
@Service // IoC (๋นˆ์œผ๋กœ ๋“ฑ๋ก)
public class BlogService {
	
	//@Autowired // DI <-- ๊ฐœ๋ฐœ์ž๋“ค์ด ๊ฐ€๋…์„ฑ ๋•Œ๋ฌธ์— ์ž‘์„ฑ์„ ํ•ด์ค€๋‹ค.
	private final PostRepository postRepository;
	
	@Transactional // (์ž‘์—…์˜ ๋‹จ์œ„) // ์“ฐ๊ธฐ ์ง€์—ฐ ์ฒ˜๋ฆฌ๊นŒ์ง€
	public Article save(ArticleDTO dto) {
		// ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ํ•„์š”ํ•˜๋‹ค๋ฉด ์ž‘์„ฑ...
		return postRepository.save(dto.toEntity());
	}
	
	// ์ „์ฒด ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ ๊ธฐ๋Šฅ
	public List<Article> findAll(){
		List<Article> articles = postRepository.findAll();
		
		return articles;
	}
	
	// ์ƒ์„ธ ๋ณด๊ธฐ ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ
	public Article findById(Integer id) {
		// Optional<T>๋Š” Java 8์—์„œ ๋„์ž…๋œ ํด๋ž˜์Šค์ด๋ฉฐ,
		// ๊ฐ’์ด ์กด์žฌํ•  ์ˆ˜๋„ ์žˆ๊ณ  ์—†์„ ์ˆ˜๋„ ์žˆ๋Š” ์ƒํ™ฉ์„ ๋ช…ํ™•ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
      return postRepository.findById(id).orElseThrow(() -> new Exception400("ํ•ด๋‹น ๊ฒŒ์‹œ๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค."));
	}
	
	@Transactional
	public Article update(Integer id, ArticleDTO dto) {
		
		// ์ˆ˜์ • ๋กœ์ง
		Article articleEntity = postRepository
				.findById(id).orElseThrow( () -> new Exception400("not found : " + id));
		
		// ๊ฐ์ฒด ์ƒํƒœ ๊ฐ’ ๋ณ€๊ฒฝ
		articleEntity.update(dto.getTitle(), dto.getContent());
		// ์˜์†์„ฑ ์ปจํ…Œ์ŠคํŠธ -
		// DB ์— save ์ฒ˜๋ฆฌ
		//postRepository.save(articleEntity);
		
		return articleEntity;
		
	}

	
	
}

ํŠธ๋žœ์žญ์…˜ ์‚ฌ์šฉ์— ์ผ๋ฐ˜์ ์ธ ๊ทœ์น™์€ ์„œ๋น„์Šค ๋ฉ”์„œ๋“œ๊ฐ€ ์—ฌ๋Ÿฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ž‘์—…์„ ํฌํ•จํ•˜๊ฑฐ๋‚˜, ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ํ†ตํ•ด ์—”ํ‹ฐํ‹ฐ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ถ”์ ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ @Transactional์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๋‹น์„ ์ˆ˜ํ–‰ ํ•œ๋‹ค.

๐Ÿ’ก
ํŠธ๋žœ์žญ์…˜๊ณผ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ๊ด€๊ณ„
โ— ํŠธ๋žœ์žญ์…˜์ด ์‹œ์ž‘๋˜๋ฉด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋„ ํ™œ์„ฑํ™”๋œ๋‹ค.
โ— ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ์กฐํšŒ๋œ ์—”ํ‹ฐํ‹ฐ๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ๊ด€๋ฆฌ๋˜๋Š” ์˜์† ์ƒํƒœ๊ฐ€ ๋œ๋‹ค.

๋”ํ‹ฐ ์ฒดํ‚น์˜ ๋ฉ”์ปค๋‹ˆ์ฆ˜:
โ— ์—”ํ‹ฐํ‹ฐ์˜ ํ•„๋“œ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋ฉด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ์ด๋ฅผ ๊ฐ์ง€ํ•ฉ๋‹ˆ๋‹ค.
โ— ๋ณ€๊ฒฝ๋œ ์—”ํ‹ฐํ‹ฐ๋Š” ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹ ์‹œ DB์— ์ž๋™์œผ๋กœ ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค.

save() ๋ฉ”์„œ๋“œ์˜ ํ•„์š”์„ฑ:
โ— ์˜์† ์ƒํƒœ์˜ ์—”ํ‹ฐํ‹ฐ๋Š” save()๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š์•„๋„ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด DB์— ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค.
โ— ์ค€์˜์† ์ƒํƒœ(detached)์˜ ์—”ํ‹ฐํ‹ฐ๋‚˜ ํŠธ๋žœ์žญ์…˜์ด ์—†๋Š” ๊ฒฝ์šฐ์—๋Š” save()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ €์žฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ฝ”๋“œ์˜ ํšจ์œจ์„ฑ
โ— ๋ถˆํ•„์š”ํ•œ save() ํ˜ธ์ถœ์„ ์ค„์ž„

 

์ฃผ์š” ๋‚ด์šฉ ์ •๋ฆฌ

 

BlogApiController ์ฝ”๋“œ ์ถ”๊ฐ€
@RequiredArgsConstructor
@RestController // @controller + @responsebody
public class BlogApiController {
	
	private final BlogService blogService;
		
	// URL , ์ฆ‰, ์ฃผ์†Œ ์„ค๊ณ„ - http://localhost:8080/api/article
	@PostMapping("/api/articles")
	public ResponseEntity<Article> addArticle(@RequestBody ArticleDTO dto) {
		// 1. ์ธ์ฆ ๊ฒ€์‚ฌ
		// 2. ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ 
		Article savedArtilce = blogService.save(dto);
		return ResponseEntity.status(HttpStatus.CREATED).body(savedArtilce);
	}
	
	
	// URL , ์ฆ‰, ์ฃผ์†Œ ์„ค๊ณ„ - http://localhost:8080/api/articles
	@GetMapping(value = "/api/articles", produces = MediaType.APPLICATION_JSON_VALUE)
	public ApiUtil<?> getAllArticles() {
		List<Article> articles = blogService.findAll();
		if(articles.isEmpty()) {
			// return new ApiUtil<>(new Exception400("๊ฒŒ์‹œ๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค."));
			throw new Exception400("๊ฒŒ์‹œ๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.");
		}
		return new ApiUtil<>(articles);
	}
	
	// URL , ์ฆ‰, ์ฃผ์†Œ ์„ค๊ณ„ - http://localhost:8080/api/articles/1
	@GetMapping(value = "/api/articles/{id}")
	public ApiUtil<?> findArtilcle(@PathVariable(name = "id") Integer id) {
		// 1. ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์ƒ๋žต 
		Article article = blogService.findById(id);
		return new ApiUtil<>(article);
	}
	
	
	// URL , ์ฆ‰, ์ฃผ์†Œ ์„ค๊ณ„ - http://localhost:8080/api/articles/1
	@PutMapping(value = "/api/articles/{id}")
	public ApiUtil<?> updateArticle(@PathVariable(name = "id") Integer id, @RequestBody ArticleDTO dto) {
		// 1. ์ธ์ฆ ๊ฒ€์‚ฌ 
		// 2. ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ 
		Article updateArticle = blogService.update(id, dto);
		return new ApiUtil<>(updateArticle);
	}

}

 

์ถ”๊ฐ€ ์ฝ์–ด ๋ณด๊ธฐ
๐Ÿ’ก
๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์€ HTTP ์š”์ฒญ์—์„œ ์ „๋‹ฌ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋ฒ„ ์ธก์˜ ์ž๋ฐ” ๊ฐ์ฒด๋‚˜ ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ์— ์ž๋™์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ณ  ํ• ๋‹นํ•˜๋Š” ๊ณผ์ •์„ ๋งํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ์ž๋Š” ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ์ถ”์ถœ ๋ฐ ๋ณ€ํ™˜ ๋กœ์ง์„ ์ง์ ‘ ๊ตฌํ˜„ํ•˜์ง€ ์•Š๊ณ ๋„ ๊ฐ„ํŽธํ•˜๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ฐธ๊ณ  ์‚ฌํ•ญ


  • DispatcherServlet:
    • Spring MVC์˜ ํ”„๋ก ํŠธ ์ปจํŠธ๋กค๋Ÿฌ(Front Controller) ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
    • ๋ชจ๋“  HTTP ์š”์ฒญ์„ ๋ฐ›์•„ ์ ์ ˆํ•œ ์ปจํŠธ๋กค๋Ÿฌ(Controller)๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.
    • ์š”์ฒญ ์ฒ˜๋ฆฌ ๊ณผ์ •์˜ ์ค‘์•™ ํ—ˆ๋ธŒ๋กœ, ์š”์ฒญ์˜ ๋ผ์šฐํŒ… ๋ฐ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์„ ์กฐ์œจํ•ฉ๋‹ˆ๋‹ค.
  • HandlerMapping:
    • ์š”์ฒญ URL๊ณผ HTTP ๋ฉ”์„œ๋“œ์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ๋ฅผ ๋งคํ•‘ํ•ฉ๋‹ˆ๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค์–ด, @PutMapping("/api/articles/{id}")์™€ ๊ฐ™์€ ๋งคํ•‘ ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ํ•ด๋‹น ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ๋ฉ”์„œ๋“œ๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.
  • HandlerAdapter:
    • ๋งคํ•‘๋œ ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ํ•„์š”ํ•œ ์ธ์ž๋ฅผ ์ œ๊ณตํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
    • HandlerMethodArgumentResolver๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ์— ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”์ธ๋”ฉํ•ฉ๋‹ˆ๋‹ค.
  • HandlerMethodArgumentResolver:
    • ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ์— ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”์ธ๋”ฉํ•˜๊ธฐ ์œ„ํ•œ ์ „๋žต์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
    • ๋Œ€ํ‘œ์ ์ธ ๊ตฌํ˜„์ฒด๋กœ๋Š” RequestParamMethodArgumentResolver, PathVariableMethodArgumentResolver, RequestBodyMethodArgumentResolver ๋“ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • HttpMessageConverter:
    • HTTP ์š”์ฒญ์˜ ๋ฐ”๋””์— ๋‹ด๊ธด ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋ฐ” ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•˜๊ฑฐ๋‚˜, ์ž๋ฐ” ๊ฐ์ฒด๋ฅผ HTTP ์‘๋‹ต์˜ ๋ฐ”๋””๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
    • Jackson ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ JSON ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋ฐ” ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•˜๋Š” MappingJackson2HttpMessageConverter๊ฐ€ ๋Œ€ํ‘œ์ ์ž…๋‹ˆ๋‹ค.