Spring Boot의 핵심 기능은 Dependency management, deployment, auto configuration 입니다. 이 세가지의 장점을 이용해서 Dependency를 아주 수월히 관리합니다.
Spring Boot Starter입니다. 스프링 부트 스타터는 거의 매번 동일한 방식으로 특정 기능을 제공한다는 입증된 전제를 바탕으로 제작된 BOM(bills of Materials)입니다. (BOM은 프로젝트 아티팩트, 라이브러리 등의 정보, 버전과 의공성 관리를 포함한 특수한 POM입니다. 여기서 POM은 빌드 도구에서 의존성을 가져오고 프로젝트 빌드에 사용하는 정보와 프로젝트 구성이 담긴 파일입니다.) 이러한 스프링 부트 스타터를 이용하면 달일 애플리케이션에 필요한 기능을 모두 제공하여 의존성을 간소화하고 애플리케이션에 기능 전체를 추가하는 데 필요한 작업을 줄여줍니다. 또 테스트, 유지보수, 업그레이드에 드는 오버헤드를 크게 줄입니다.💡 Mashalling/Unmashalling
객체의 메모리 표현 방식을 저장이나 전송에 알맞은 데이터 형식으로 변환하는 프로세스입니다. 데이터를 서로 다른 컴퓨터 프로그램 간 혹은 같은 프로그램의 다른 부분에 옮길 때 사용합니다.
오래전에는 프로그램을 만들고 배포하는데 많은 복잡한 절차를 거쳐야 합니다. 복잡한 과정을 짧으면 한줄 혹은 두줄로 줄일 수 있습니다. 이를 위해 의도된 형식과 전달 형식을 유지하면서 중첩된 JAR을 만드는 방식을 고안했습니다. 이러한 중첩으로 버전간의 충돌을 피하고 JAR의 내용도 간단히 추출합니다.
프로그래밍은 겹치는 코드가 많이 나오게 됩니다. 이렇게 코드를 구성하면서 겹치는 부분을 자동으로 설정이 되어 특정 작업헤서 작성해야 할 코드가 간소화됩니다. Bean이라는 것을 제공하여, 사용자를 대신해 자동 설정 작업을 수행합니다.
Spring MVC는 뷰가 서버 렌더링된 웹페이조 제공된다는 가정하에, 데이터와 데이터를 전송하는 부분과 데이터를 표현하는 부분을 분리해 생성합니다. 이렇게 분리하는데 @Controller이 도움이 됩니다.
@Controller 어노테이션은 @Component의 스테레오 타입 별칭입니다. 애플리케이션을 실행시, Spring Bean(Bean은 애플리케이션 내 객체로서 스프링 IoC 컨테이너에 의해 생성되고 관리 된다.), @Controller annotation 이 붙은 클래스로 부터 생성이 됩니다.

@ResponseBody를 클래스나 메서드에 추가해서 JSON, XML 같은 데이터 형식 처럼 형식화된 응답을 반환하도록 클래스에 지시할 수 있습니다. 이렇게 하면 메서드의 객체/Iterable 변환ㄱ밧이 웹 응답의 전체 바디가 되고 모델의 일부로 반환되지 않습니다.
@RestController는 위의 Controller와 ResponseBody annotation을 합쳐 코드를 더욱 단순하고 명확하게 만들게 됩니다. 또한 클래스에 해당 어노테이션을 달아서 REST API를 만들 수 있습니다.
@RequestMapping을 대신할 수 있는 편한 어노테이션
@GetMapping
@PutMapping
@PatchMapping
@DeleteMapping
이 같은 Mapping annotation은 클래스나 메서드 수준에서 적용하며 URL 경로를 추가로 적어주면 됩니다. 예시는 위의 코드와 같습니다.
@RestController
class RestApiDemoController{
private List<Coffee> coffees = new ArrayList<>();
public RestApiDemoController(){
coffees.addAll(List.of(
new Coffee("Grazie"),
new Coffee("MEGA"),
new Coffee("Compose"),
new Coffee("PAIK")
));
}
// 코드의 동작 여부를 확인하기 위해 최상위 제네릭 List 안에
// 객체를 선언하고 넣어줍니다.
// List.of() 메서드는 Java 9에서 도입된 팩토리 메서드로, 불변 리스트를 생성합니다. 이 메서드에 전달된 인자들은 리스트의 요소가 됩니다.
// 한번 넣으면 변경이 불가하다.
// @RequestMapping(value = "/coffees", method = RequestMethod.GET) // 해당 getCoffees 메서드가 /coffee URL에만 응답하게 제한하는 코드
// Iterable<Coffee> getCoffees(){
// return coffees;
// }
@GetMapping("/coffees")
Iterable<Coffee> getCoffees(){
return coffees;
}
@GetMapping("/coffees/{id}") // 단일 아이템 조회 메서드, id는 URI 변수
// @PathVariable annotation이 있는 id 매개변수를 통해 해당 메서드에 id 값이 전달된다.
Optional<Coffee> getCoffeeById(@PathVariable String id){
for (Coffee c : coffees) {
if(c.getId().equals(c)){
return Optional.of(c);
}
}
return Optional.empty();
}
}
Post는 리소스의 세버(일반적으로 JSON 형식)를 제공합니다. 해당 서비스에 Post 요청을 해서 지정된 URI에 리소스를 생성합니다.
@PostMapping("/coffees")
Coffee postCoffee(@RequestBody Coffee coffee){
coffees.add(coffee);
return coffee;
}
Spring의 자동 마샬링에 의해 해당 커피 정보를 Coffee 객체로서 받습니다. 그리고 해당 객체를 커피 목록에 추가 합니다. Coffee 객체는 스프링 부트에 의해 언마샬링되어 요청한 애플리케이션이나 서비스로 반환됩니다.
일반적으로 PUT 요청은 파악된 URI를 통해 기존 리소스의 업데이트에 사용됩니다.
@PutMapping("/coffees/{id}") // 요청하는 URL의 형태
/*
1. @PathVariable은 해당 메서드의 변수가 경로 변수임을 나타낸다.
2. @RequestBody는 HTML의 Body 부분을 Java로 변환하여 메서드 파라미터로 만듭니다.
*/
Coffee putCoffee(@PathVariable String id, @RequestBody Coffee coffee){
int coffeeIndex = -1;
for(Coffee c : coffees){
if(c.getId().equals(id)){
coffeeIndex = coffees.indexOf(c);
coffees.set(coffeeIndex, coffee);
}
}
return (coffeeIndex == -1) ? postCoffee(coffee) : coffee;
}
HTTP의 DELETE 요청은 리소스를 삭제합니다. 다음 오는 코드는 @PathVarialble로 커피 식별자인 id를 받아서 Collection 메서드의 removeIf를 사용해 해당 커피를 목록에서 제거합니다. removeIf는 Predicate 값을 받습니다. 즉, 목록에 제거할 커피가 존재하면 True or False를 반환하는 람다입니다.
@DeleteMapping("/coffees/{id}")
void deleteCoffee(@PathVariable String id){
coffees.removeIf(c -> c.getId().equals(id));
}
위에서 반복되는 부분인 /coffees를 줄이기 위해 @RequestMapping("/coffees")를 적습니다.
완성된 전체 코드
@RestController
@RequestMapping("/coffees")
class RestApiDemoController{
private List<Coffee> coffees = new ArrayList<>();
public RestApiDemoController(){
coffees.addAll(List.of(
new Coffee("Grazie"),
new Coffee("MEGA"),
new Coffee("Compose"),
new Coffee("PAIK")
));
}
// 코드의 동작 여부를 확인하기 위해 최상위 제네릭 List 안에
// 객체를 선언하고 넣어줍니다.
// List.of() 메서드는 Java 9에서 도입된 팩토리 메서드로, 불변 리스트를 생성합니다. 이 메서드에 전달된 인자들은 리스트의 요소가 됩니다.
// 한번 넣으면 변경이 불가하다.
// @RequestMapping(value = "/coffees", method = RequestMethod.GET) // 해당 getCoffees 메서드가 /coffee URL에만 응답하게 제한하는 코드
// Iterable<Coffee> getCoffees(){
// return coffees;
// }
@GetMapping
Iterable<Coffee> getCoffees(){
return coffees;
}
@GetMapping("/{id}") // 단일 아이템 조회 메서드, id는 URI 변수
// @PathVariable annotation이 있는 id 매개변수를 통해 해당 메서드에 id 값이 전달된다.
Optional<Coffee> getCoffeeById(@PathVariable String id){
for (Coffee c : coffees) {
if(c.getId().equals(c)){
return Optional.of(c);
}
}
return Optional.empty();
}
@PostMapping
Coffee postCoffee(@RequestBody Coffee coffee){
coffees.add(coffee);
return coffee;
}
@PutMapping("/{id}") // 요청하는 URL의 형태
/*
1. @PathVariable은 해당 메서드의 변수가 경로 변수임을 나타낸다.
2. @RequestBody는 HTML의 Body 부분을 Java로 변환하여 메서드 파라미터로 만듭니다.
*/
Coffee putCoffee(@PathVariable String id, @RequestBody Coffee coffee){
int coffeeIndex = -1;
for(Coffee c : coffees){
if(c.getId().equals(id)){
coffeeIndex = coffees.indexOf(c);
coffees.set(coffeeIndex, coffee);
}
}
return (coffeeIndex == -1) ? postCoffee(coffee) : coffee;
}
@DeleteMapping("/{id}")
void deleteCoffee(@PathVariable String id){
coffees.removeIf(c -> c.getId().equals(id));
}
}
references
1. 김영한님 강의자료
2. 스프링 부트 기초