Skip to content

Latest commit

ย 

History

History
218 lines (151 loc) ยท 12.4 KB

Ch_6.md

File metadata and controls

218 lines (151 loc) ยท 12.4 KB

6์žฅ. REST ์„œ๋น„์Šค ์ƒ์„ฑํ•˜๊ธฐ

๐Ÿ‘ฉ๐Ÿพโ€๐Ÿซ ๋ฐฐ์šฐ๋Š” ๋‚ด์šฉ

  • ์Šคํ”„๋ง MVC์—์„œ REST ์—”๋“œํฌ์ธํŠธ ์ •์˜ํ•˜๊ธฐ
  • ํ•˜์ดํผ๋งํฌ REST ๋ฆฌ์†Œ์Šค ํ™œ์„ฑํ™”ํ•˜๊ธฐ
  • ๋ ˆํฌ์ง€ํ† ๋ฆฌ ๊ธฐ๋ฐ˜์˜ REST ์—”๋“œํฌ์ธํŠธ ์ž๋™ํ™”

6.1 REST ์ปจํŠธ๋กค๋Ÿฌ ์ž‘์„ฑํ•˜๊ธฐ

SPA(Single-Page Application)์ด ๋“ฑ์žฅํ•˜๋ฉด์„œ ์„œ๋ฒ„์—์„œ๋Š” ์—ฌ๋Ÿฌ๊ฐœ์˜ ๋ทฐ๋ฅผ ๋ฐ˜ํ™˜ํ•ด์ฃผ๋Š” ๊ฒƒ์ด ์•„๋‹Œ ๋ฐ์ดํ„ฐ๋งŒ ์ œ๊ณตํ•˜๋ฉฐ, ์ด๋ฅผ ํด๋ผ์ด์–ธํŠธ ๋‹จ์—์„œ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋œ๋‹ค. SPA์—์„œ๋Š” ํ”„๋ฆฌ์  ํ…Œ์ด์…˜ ๊ณ„์ธต(๋ทฐ)์ด ์•„๋ž˜ ๊ณ„์ธต๊ณผ๋Š” ๊ฑฐ์˜ ๋…๋ฆฝ์ ์ด๋ฏ€๋กœ ๋‹ค๋ฅธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜(๋ชจ๋ฐ”์ผ)์—์„œ๋„ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ํ•˜์ง€๋งŒ, ํ•ญ์ƒ SPA๊ฐ€ ์ •๋‹ต์€ ์•„๋‹ˆ๋ฉฐ ๋‹จ์ˆœํ•˜๊ฒŒ ์ •๋ณด๋งŒ ๋ณด์—ฌ์ฃผ๋Š” ํŽ˜์ด์ง€์ผ ๊ฒฝ์šฐ MPA(Multi-Page Application)์ด ์ข‹์„ ์ˆ˜ ์žˆ๋‹ค.

์•ต๊ทค๋Ÿฌ ํ˜น์€ ๋ฆฌ์•กํŠธ์™€ ๊ฐ™์€ ํ”„๋ก ํŠธ์˜ ํ”„๋ ˆ์ž„์›Œํฌ ํ˜น์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ๋Š” SPA๋ฅผ ๊ตฌ์ถ•ํ•œ๋‹ค๋ฉด, ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ฑฐ๋‚˜ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด์„œ REST API๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•œ๋‹ค. 2์žฅ์—์„œ ๋ณด์•˜๋˜ @GetMapping ํ˜น์€ @PostMapping ๋“ฑ ์—ฌ๋Ÿฌ๊ฐ€์ง€ HTTP ์š”์ฒญ-์ฒ˜๋ฆฌ ์• ๋…ธํ…Œ์ด์…˜๋“ค์€ REST API์—์„œ๋„ ์‚ฌ์šฉ๋œ๋‹ค.

@RestController

@RestController ์• ๋…ธํ…Œ์ด์…˜์€ @Controller์™€ @Service์™€ ๊ฐ™์€ ์Šคํ…Œ๋ ˆ์˜ค ํƒ€์ž…์˜ ์• ๋…ธํ…Œ์ด์…˜์œผ๋กœ ์ปดํฌ๋„ŒํŠธ ์Šค์บ”์— ์˜ํ•ด ๋ฐœ๊ฒฌ๋˜์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค. ๋˜, ์ปจํŠธ๋กค๋Ÿฌ์˜ ๋ชจ๋“  HTTP ์š”์ฒญ ์ฒ˜๋ฆฌ ๋ฉ”์„œ๋“œ์˜ ๋ฐ˜ํ™˜๊ฐ’์ด Body์— ๋‹ด๊ธด๋‹ค๋Š” ๊ฒƒ์„ ์Šคํ”„๋ง์— ์•Œ๋ ค์ค€๋‹ค. ์ฆ‰, ๋ฐ˜ํ™˜๊ฐ’์ด ๋ทฐ๋กœ ์ด์–ด์ง€๋Š” ๊ฒƒ์ด ์•„๋‹Œ HTTP ์‘๋‹ต์œผ๋กœ ๋ธŒ๋ผ์šฐ์ €์— ์ „๋‹ฌ๋˜์–ด ๋‚˜ํƒ€๋‚œ๋‹ค.

๊ผญ ์ด ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•ด์•ผํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค. ๊ธฐ์กด @Controller ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•ด์„œ๋„ ๋˜‘๊ฐ™์€ ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š”๋ฐ ์ด๋•Œ๋Š” @ResonseBody ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. @RestController์˜ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด @Controller์™€ @ResonseBody ์• ๋…ธํ…Œ์ด์…˜์ด ๋‚ด๋ถ€์—์„œ ์ง€์ •๋˜์–ด ์žˆ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {

	/**
	 * The value may indicate a suggestion for a logical component name,
	 * to be turned into a Spring bean in case of an autodetected component.
	 * @return the suggested component name, if any (or empty String otherwise)
	 * @since 4.0.1
	 */
	@AliasFor(annotation = Controller.class)
	String value() default "";

}

์ด์™ธ์—๋„ ResponseEntity ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์กด์žฌํ•˜๊ธฐ๋„ ํ•œ๋‹ค.

@CrossOrigin

๋ธŒ๋ผ์šฐ์ €์—์„œ๋Š” ๋ณด์•ˆ ๋ชฉ์ ์œผ๋กœ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ์— ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์„ ์ฐจ๋‹จํ•˜๊ณ  ์žˆ๋‹ค(SOP(Same-Origin Poicy)). ํ•˜์ง€๋งŒ CORS(Cross-Origin Resource Sharing)๋ฅผ ํ†ตํ•ด ์˜ˆ์™ธ์ ์ธ ์ƒํ™ฉ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋งํฌ๋ฅผ ํƒ€๊ณ  ๋“ค์–ด๊ฐ€ ๋ณด๊ธฐ๋ฅผ ๋ฐ”๋ž€๋‹ค. ์Šคํ”„๋ง์—์„œ๋Š” @CrossOrigin ์• ๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•ด ์‰ฝ๊ฒŒ CORS๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

PUT vs PATCH

๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์„ ์œ„ํ•ด HTTP ๋ฉ”์„œ๋“œ๋Š” PUT๊ณผ PATCH๊ฐ€ ์กด์žฌํ•œ๋‹ค. ๋ฌด์—‡์ด ๋‹ค๋ฅธ์ ์ผ๊นŒ?

PUT์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•˜๊ธด ํ•˜์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” GET๊ณผ ๋ฐ˜๋Œ€์˜ ์˜๋ฏธ๋ฅผ ๊ฐ–๋Š”๋‹ค. GET์€ ์„œ๋ฒ„์—์„œ ํด๋ผ์ด์–ธํŠธ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•˜๋Š” ๋ฐ˜๋ฉด, PUT ์š”์ฒญ์€ ํด๋ผ์ด์–ธํŠธ์—์„œ ์„œ๋ฒ„๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•œ๋‹ค.

์ด๋Ÿฌํ•œ ๊ด€์ ์—์„œ PUT์€ ๋ฐ์ดํ„ฐ ์ „์ฒด๋ฅผ ๊ต์ฒดํ•˜๋Š” ๊ฒƒ์ด๋ฉฐ, PATCH์˜ ๋ชฉ์ ์€ ๋ฐ์ดํ„ฐ์˜ ์ผ๋ถ€๋ถ„๋งŒ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๋งŒ์•ฝ ์ผ๋ถ€๋ถ„์„ ๋ณ€๊ฒฝํ•˜๋Š”๋ฐ PUT ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๋‹ค๋ฅธ ์†์„ฑ๋“ค์€ null๋กœ ๋ณ€๊ฒฝ์ด ๋œ๋‹ค.

PATCH๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ๋‘๊ฐ€์ง€ ์ œ์•ฝ์ด ์กด์žฌํ•œ๋‹ค.

  1. ํŠน์ • ํ•„๋“œ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์œผ๋กœ null์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด, ํด๋ผ์ด์–ธํŠธ์—์„œ๋Š” ์ด๋ฅผ ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ํ•„์š”ํ•˜๋‹ค.
  2. ์ปฌ๋ ‰์…˜์— ์ €์žฅ๋œ ํ•ญ๋ชฉ์„ ์‚ญ์ œ ํ˜น์€ ์ถ”๊ฐ€ํ•  ๋ฐฉ๋ฒ•์€ ์—†๋‹ค. ์ด๋ฅผ ํ•˜๋ ค๋ฉด ์ „์ฒด๋ฅผ ์ „์†กํ•ด์•ผ ํ•œ๋‹ค.

6.2 ํ•˜์ดํผ๋ฏธ๋””์–ด ์‚ฌ์šฉํ•˜๊ธฐ

RESTํ•œ API๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ **HATEOAS(**Hypermedia As The Engine Of Application State)๋ฅผ ๋งŒ์กฑํ•ด์•ผ ํ•œ๋‹ค. ์ด๋Š” API๋กœ๋ถ€ํ„ฐ ๋ฐ˜ํ™˜๋˜๋Š” ๋ฆฌ์†Œ์Šค์— ํ•ด๋‹นํ•˜์—ฌ ๊ด€๋ จ๋œ ํ•˜์ดํผ๋งํฌ๋“ค์ด ํฌํ•จ๋˜์–ด์•ผ ํ•œ๋‹ค.

HAL(Hypertext Application Language)

HAL์€ HATEOAS์˜ format์ค‘ ํ•˜๋‚˜๋กœ JSON ๋˜๋Š” XML ์‘๋‹ต์— ํ•˜์ดํผ๋งํฌ๋ฅผ ํฌํ•จ์‹œํ‚ฌ ๋•Œ ์ฃผ๋กœ ์‚ฌ์šฉ๋œ๋‹ค. ์‘๋‹ต์˜ ๋ชจ์Šต์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

{
  "_embedded" : 
  {
    "tacoResourceList" : 
    [
      {
        "name": "Veg-Out",
      	"createdAt": "~~",
      	"ingredients": 
      	[
      		{
      			"name": "Flour Tortilla", 
      			"type": "WRAP",
      			"_links" : 
      			{
      				"self" : {"href": "http://localhost:8080/ingredients/FLTO"}
      			}
      		}
          ...
    		]
      }
    ]
  },
  "_links": {
    "recents": {
      "href": "http://localhost:8080/design/recent"
    }
  }
}

์ด์™€ ๊ฐ™์ด ์ „๋‹ฌํ•˜๋ ค๋Š” ๋ฐ์ดํ„ฐ ์•ˆ์— _links ๋ผ๋Š” ์†์„ฑ์„ ํฌํ•จํ•˜๊ฒŒ ๋œ๋‹ค. ์ด๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๋Š” ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ API๋ฅผ ์ˆ˜ํ–‰ ํ•  ์ˆ˜ ์žˆ๋Š” ํ•˜์ดํผ๋งํฌ๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ๋‹ค.

์ด๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ ์šฐ๋ฆฌ๋Š” spring-boot-stater-hatedoas ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด๋œ๋‹ค.

๋งํฌ ์ถ”๊ฐ€ํ•˜๊ธฐ

์ฑ…์—์„œ ํ•˜์ดํผ๋งํฌ ๋ฆฌ์†Œ์Šค๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ํƒ€์ž…์œผ๋กœ Resource์™€ Resources ๋ฅผ ์„ค๋ช…ํ•˜๊ณ  ์žˆ๋‹ค. ์ด๋Š” EntityModel ๊ณผ CollectionModel ์œผ๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ๊ธฐ์— ๋ณ€๊ฒฝ๋œ ์ด๋ฆ„์œผ๋กœ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋‹ค(์ž์„ธํ•œ ๋‚ด์šฉ์„ ๋งํฌ ์ฐธ๊ณ ). EntityModel ํƒ€์ž…์€ ๋‹จ์ผ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ทธ๋ฆฌ๊ณ  CollectionModel ํƒ€์ž…์€ ๋ฆฌ์†Œ์Šค ์ปฌ๋ ‰์…˜์„ ๋‚˜ํƒ€๋‚ด๋ฉฐ, ์ด ๋‘˜์€ ๋‹ค๋ฅธ ๋ฆฌ์†Œ์Šค๋ฅผ ๋งํฌํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋ฅผ ํ†ตํ•ด์„œ ์šฐ๋ฆฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋งํฌ๋ฅผ ์ถ”๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋‹ค.

@GetMapping("/recent")
public CollectionModel<EntityModel<Taco>> recentTacos() {
	PageRequest page = PageRequest.of(0, 12, Sort.by("createdAt").descending());
        
	List<Taco> tacos = tacoRepo.findAll(page).getContent();
	CollectionModel<EntityModel<Taco>> recentResources = CollectionModel.wrap(tacos);
	recentResources.add(Link.of("http://localhost:8080/design/recent","recents"));
	return recentResources;
}

ํ•˜๋‚˜ํ•˜๋‚˜ ํ•˜๋“œ์ฝ”๋”ฉ์œผ๋กœ ๋งํฌ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค๋ฉด ๊ต‰์žฅํžˆ ์ข‹์ง€ ์•Š์€ ๋ฐฉ๋ฒ•์ด๋‹ค. ์Šคํ”„๋ง HATEOAS์—์„œ๋Š” ๋งํฌ ๋นŒ๋”๋ฅผ ์ œ๊ณตํ•˜์—ฌ URL์„ ํ•˜๋“œ์ฝ”๋”ฉํ•˜์ง€ ์•Š๋Š” ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•œ๋‹ค. WebMvcLinkBuilder ์ด ๋ฐ”๋กœ HATEOAS ๋งํฌ ๋นŒ๋”์—์„œ ๊ฐ€์žฅ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.(์ฑ…์—์„œ๋Š” ControllerLinkBuilder ๋ผ๊ณ  ์ ํ˜€์žˆ๋Š”๋ฐ ์ด๊ฐ€ WebMvcLinkBuilder ๋กœ ๋ณ€๊ฒฝ) ๋งํฌ ๋นŒ๋”๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์œ„ ์ฝ”๋“œ๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

recentResources.add(
  WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(DesignTacoController.class)
  																					.recentTacos())
  								 .withRel("recents"));

์œ„ WebMvcLinkBuilder.methodOn ๋ฉ”์„œ๋“œ๋Š” DesignTacoController ๋ฅผ ์ธ์ž๋กœ ๋ฐ›์•„ recentTacos() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค. ๋”ฐ๋ผ์„œ ํ•ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ์˜ ๊ธฐ๋ณธ ๊ฒฝ๋กœ์™€ recentTacos() ์˜ ๋งคํ•‘ ๊ฒฝ๋กœ๋ฅผ ๋ชจ๋‘ ๊ฒฐ์ •ํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•œ๋‹ค.

๋ฆฌ์†Œ์Šค ์–ด์…ˆ๋ธ”๋Ÿฌ ์ƒ์„ฑ

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

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ ๋ฆฌ์†Œ์Šค๋“ค์„ Resource ๊ฐ์ฒด๋กœ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ์‹ ๋Œ€์‹ ์— ๋งํฌ๋ฅผ ์ถ”๊ฐ€๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์„ ๊ฑฐ์น  ๊ฒƒ์ด๋‹ค. ์ด ๋ณ€ํ™˜์„ ๋„์™€์ฃผ๋Š” ๊ฒƒ์ด ๋ฐ”๋กœ ๋ฆฌ์†Œ์Šค ์–ด์…ˆ๋ธ”๋Ÿฌ์ด๋‹ค.

public class TacoResourceAssembler extends RepresentationModelAssemblerSupport<Taco, TacoResource>{

  // ์ƒ์œ„ ํด๋ž˜์Šค๋ฅผ ํ˜ธ์ถœ, TacoResource๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๋งŒ๋“ค์–ด์ง€๋Š” ๋งํฌ์˜ ๊ธฐ๋ณธ ๊ฒฝ๋กœ๋ฅผ ๊ฒฐ์ •ํ•˜๊ธฐ ์œ„ํ•ด DesignTacoController๋ฅผ ์‚ฌ์šฉ
	public TacoResourceAssembler() {
		super(DesignTacoController.class, TacoResource.class);
	}

  // ๋งŒ์•ฝ TacoResource์˜ ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๊ฐ€ ์กด์žฌํ•œ๋‹ค๋ฉด ์ƒ๋žต ๊ฐ€๋Šฅ
	@Override
	protected TacoResource instantiateModel(Taco taco) {
		return new TacoResource(taco);
	}

  // Taco ๊ฐ์ฒด๋กœ TacoResource ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋ฉด์„œ Taco ๊ฐ์ฒด์˜ id ์†์„ฑ ๊ฐ’์œผ๋กœ ์ƒ์„ฑ๋˜๋Š” self ๋งํฌ๊ฐ€ URL์— ์ž๋™ ์ง€์ •
	@Override
	public TacoResource toModel(Taco entity) {
		return createModelWithId(taco.getId(), taco);
	}
}

์ฑ…์—์„œ์˜ ResourceAssemblerSupport ๋Š” RepresentationModelAssemblerSupport ๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ๋‹ค.


6.3 ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜ ์„œ๋น„์Šค ํ™œ์„ฑํ™”

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ REST

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ REST๋Š” ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ์˜ ๋˜ ๋‹ค๋ฅธ ๋ชจ๋“ˆ์ด๋ฉฐ, ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ์„ฑํ•˜๋Š” ๋ ˆํฌ์ง€ํ† ๋ฆฌ์— REST API๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•œ๋‹ค. ์ด๋Š” spring-boot-starter-data-rest ์˜์กด์„ฑ๋งŒ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉ ์ค‘์ธ ํ”„๋กœ์ ํŠธ์—์„œ REST API๋ฅผ ๋…ธ์ถœ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ๊ธฐ์กด์— ์กด์žฌํ•˜๋˜ @RestController์™€ ์—”๋“œ ํฌ์ธํŠธ๊ฐ€ ๊ฒน์น  ์ˆ˜ ๋„ ์žˆ์œผ๋‹ˆ ์ฃผ์˜ํ•ด์•ผ ๋œ๋‹ค.

http://localhost:8080/ingredients

์œ„์˜ ์ฃผ์†Œ์— ๋“ค์–ด๊ฐ€ ๋ณด๋ฉด, ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ REST์—์„œ ์ž๋™-๊ตฌ์„ฑ์œผ๋กœ ์ด๋ฏธ API๋ฅผ ๋งŒ๋“ค์–ด ๋†“์€๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

์œ„ ๊ทธ๋ฆผ์€ ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด์ง„ API์˜ ์ผ๋ถ€๋งŒ ์บก์ณํ•˜์˜€๋‹ค. ์ด๋ ‡๊ฒŒ ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด์ง„ ์—”๋“œํฌ์ธํŠธ๋“ค์€ GET, POST, PUT, DELETE ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์›ํ•˜๋ฏ€๋กœ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์ž‘์—…์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ถ”๊ฐ€์ ์œผ๋กœ ํ•  ์ผ์€ ์šฐ๋ฆฌ๊ฐ€ ์ž‘์„ฑํ•œ ์ปจํŠธ๋กค๋Ÿฌ์˜ ์—”๋“œํฌ์ธํŠธ์™€ ์ถฉ๋Œํ•˜์ง€ ์•Š๊ฒŒ ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋˜๋Š” API์˜ ๊ธฐ๋ณธ ๊ฒฝ๋กœ๋ฅผ ์„ค์ •ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค. ์ด๋Š” application.yml ํŒŒ์ผ์— spring.data.rest.base-path ์†์„ฑ์„ ์„ค์ •ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

๋ฆฌ์†Œ์Šค ๊ฒฝ๋กœ์™€ ๊ด€๊ณ„ ์ด๋ฆ„ ์กฐ์ •

์ž๋™์œผ๋กœ API๋ฅผ ๊ตฌ์„ฑํ•  ๋•Œ, ์ด๋ฆ„์˜ ๊ฒฝ์šฐ ์Šคํ”„๋ง์—์„œ ์•Œ์•„์„œ ์ •ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ธฐ๋ณธ์ ์œผ๋กœ๋Š” ํ•ด๋‹น ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค ์ด๋ฆ„์˜ ๋ณต์ˆ˜ํ˜•์„ ์‚ฌ์šฉํ•œ๋‹ค.(taco โ†’ tacoes, ingredient โ†’ ingredients)

์ด๋Š” ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค์— @RestResource ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

@Data
@Entity
@RestResource(rel="tacos", path="tacos")

์œ„์™€ ๊ฐ™์ด ์„ค์ •์„ ํ•œ๋‹ค๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ ์ƒ๊ธฐ๋Š” tacoes๊ฐ€ ์•„๋‹Œ tacos๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

ํŽ˜์ด์ง•๊ณผ ์ •๋ ฌ

ํ™ˆ ๋ฆฌ์†Œ์Šค(๋ชจ๋“  ์—”ํ‹ฐํ‹ฐ์˜ ๋งํฌ๋ฅผ ๋ณด์—ฌ์คŒ)์˜ ๋ชจ๋“  ๋งํฌ๋Š” ์„ ํƒ์  ๋งค๊ฐœ๋ณ€์ˆ˜์ธ page, size, sort๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ์ด๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜๊ธฐ๊ฒŒ ๋˜๋ฉด ์›ํ•˜๋Š” ํŽ˜์ด์ง€, ํŽ˜์ด์ง€์˜ ํฌ๊ธฐ, ์ •๋ ฌ ์ƒํƒœ๋ฅผ ์ ์šฉํ•˜์—ฌ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

http://localhost:8080/api/tacos?size=5&page=1

์œ„์™€ ๊ฐ™์ด ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด ํŽ˜์ด์ง€ ํฌ๊ธฐ๊ฐ€ 5์ด๋ฉด์„œ 2๋ฒˆ์งธ ํŽ˜์ด์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๊ฒŒ ๋œ๋‹ค.(ํŽ˜์ด์ง€๋Š” 0๋ถ€ํ„ฐ ์‹œ์ž‘) ์ด์™ธ์—๋„ ์ „์ฒด๊ฐ€ ๋ช‡๊ฐœ์ธ์ง€, ์ „์ฒด ํŽ˜์ด์ง€์ˆ˜๋Š” ๋ช‡์ธ์ง€์— ๋Œ€ํ•œ ํ‘œ์‹œ๋„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

์ปค์Šคํ…€ ์—”๋“œํฌ์ธํŠธ ์ถ”๊ฐ€

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

  1. ์ƒˆ๋กœ ๋งŒ๋“  ์—”๋“œํฌ์ธํŠธ๋Š” ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ REST์˜ ๊ธฐ๋ณธ ๊ฒฝ๋กœ๋กœ ๋งคํ•‘๋˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ, ๊ธฐ๋ณธ ๊ฒฝ๋กœ๋ฅผ ํฌํ•จํ•˜์—ฌ ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ๊ธฐ๋ณธ ๊ฒฝ๋กœ๊ฐ€ ์•ž์— ๋ถ™๋„๋ก ๋งคํ•‘ํ•ด์•ผ ๋œ๋‹ค. ๋งŒ์•ฝ ๊ธฐ๋ณธ ๊ฒฝ๋กœ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ํ•ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ์˜ ๋งคํ•‘ ๋˜ํ•œ ์ผ์น˜ ์‹œ์ผœ์ฃผ์–ด์•ผ ํ•œ๋‹ค.
  2. ์ƒˆ๋กœ ์ •์˜ํ•œ ์—”๋“œํฌ์ธํŠธ๋Š” ํ•˜์ดํผ๋งํฌ์— ์ž๋™์œผ๋กœ ํฌํ•จ๋˜์ง€ ์•Š๋Š”๋‹ค.

์ปจํŠธ๋กค๋Ÿฌ์— ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ REST ๊ธฐ๋ณธ ๊ฒฝ๋กœ๋ฅผ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด @RepositoryRestController ์• ๋…ธํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ•œ๋‹ค. ์ด๋Š” @RestController ์™€ ๊ฑฐ์˜ ํก์‚ฌํ•˜๊ฒŒ ์ƒ๊ฒผ๋Š”๋ฐ ๋™์ผํ•˜๊ฒŒ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค. @RepositoryRestController ๋Š” ๋ฐ˜ํ™˜๊ฐ’์„ ์š”์ฒญ ๋ฐ”๋””์— ์ž๋™์œผ๋กœ ๋„ฃ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— @ResponseBody ์• ๋…ธํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜, ResponseEntitiy๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค.