배움과 기록의 장
[Spring] MVC - API 계층 본문
2023.2.13 작성
🔸 API 계층 구현
Spring MVC에서 클라이언트 요청의 최종목적지인 API계층, 즉 Controller 클래스를 설계하고 구현한다.
🔸 프로젝트 환경 구성
1. 샘플 프로젝트 소개
1) 애플리케이션 이름: 커피주문 웹 애플리케이션 (커피 주문을 위해 필요한 정보를 제공하기 위한 서버용 웹애플리케이션)
2) 애플리케이션 경계(앱 기능을 특정 범위로 제한하는 것) : 커피만 주문할 수 있도로 기능 제한
3) 커피 주문을 위해 필요한 정보: Coffee , Member, Order
4) 커피 주문 앱에서 제공할 기능: 주인이 커피정보를 등록,수정,조회,삭제하는 기능 등
2. 샘플 프로젝트 환경 구성
- Project: Gradle-Groovy
- Language: Java
- Spring Boot: 2.7.8
- Packaging: Jar
- Java: 11
- Dependencies:
Lombok(애너테이션을 통해 자주 사용하는 자바코드를 자동으로 구성해주는 라이브러리),
Spring Web(’Spring Web’ Spring 기반의 웹 애플리케이션을 개발하는데 필요한 의존 라이브러리들을 자동으로 설정해주는 모듈들을 포함)
🔸 Controller 클래스 설계 및 구조 생성
1) 샘플 프로젝트의 Java 패키지 구조 잡기
- 주로 사용되는 구조에는,
- 기능기반 패키지 구조(package-by- feature): coffee 기능, member 기능 등.. 각 기능 안에 계층별 클래스들이 모여있음
- 계층 기반 패키지 구조(package-by-layer): API계층(controller, dto), 서비스계층(model, service), 데이터계층(repository)
- 앱의 요구사항이나 특서에 따라 상황에 맞게 사용하면 되지만, Spring Boot 팀에서는 테스트와 리팩토링이 용이하고, 향후에 마이크로 서비스 시스템으로의 분리가 상대적으로 용이한 기능 기반 패키지 구조 사용을 권장
- 샘플 프로젝트 패키지 구조 (대략)
- coffee
- coffeeController
- coffeeService
- coffeeRepository
- member
- memberController
- memberService
- memberRepository
- order
- orderController
- orderService
- orderRepository
2) 앤트리 포인트 클래스 작성 (Entrypoint, 애플리케이션 시작점)
- Spring Boot 기반의 애플리케이션이 정상적으로 실행되기 위해서 가장 먼저 해야될 일
- ‘Spring Initializr’를 통해 생성한 프로젝트에는 main() 메서드가 포함된 엔트리포인트 클래스가 이미 작성
- 엔트리 포인트 클래스
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication // (a)
public class Section3Week1Application {
public static void main(String[] args) {
// (b)
SpringApplication.run(Section3Week1Application.class, args);
}
}
- (a) @SpringBootApplication
- 자동 구성을 활성화
- 애플리케이션 패키지 내에서 @Component가 붙은 클래스를 검색한 후(scan), Spring Bean으로 등록하는 기능을 활성화
- @Configuration 이 붙은 클래스를 자동으로 찾아주고, 추가적으로 Spring Bean을 등록하는 기능을 활성화
- (b) SpringApplication.run(Section3Week1Application.class, args);
- Spring 애플리케이션을 부트스트랩하고, 실행하는 역할
- 부트스트랩이란? 앱 실행 전 여러가지 설정 작업을 수행하여 실행 가능한 애플리케이션으로 만드는 단계를 의미
3) 샘플 프로젝트의 Controller 구조 작성
- member 패키지 안의 memberController (coffee, order 도 마찬가지)
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController // (a)
@RequestMapping("/v1/members") // (b)
public class MemberController {
}
- (a) @RestController
- 특정 클래스에 @RestController 를 추가하면, Spring MVC에서는 해당 클래스가 REST API의 리소스를 처리하기 위한 API 엔드포인트로 동작함을 정의
- @RestController 가 추가된 클래스는 애플리케이션 로딩 시, Spring Bean으로 등록
- (b) @RequestMapping("/v1/members")
- 클라이언트의 요청과 클라이언트 요청을 처리하는 핸들러 메서드(Handler Method)를 매핑해주는 역할
- Controller 클래스 레벨에 추가하여 클래스 전체에 사용되는 공통 URL(Base URL) 설정
🔸 핸들러 메서드
1) 핸들러 메서드란? Controller 클래스 내 클라이언트의 요청을 처리하는 메서드
2) 핸들러 메서드 적용
- member 패키지 안의 memberController (coffee, order 도 마찬가지)
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping(value = "/v1/members", produces = {MediaType.APPLICATION_JSON_VALUE}) //(a)
public class MemberController {
@PostMapping //(b)
public String postMember(@RequestParam("email") String email, //(c)
@RequestParam("name") String name,
@RequestParam("phone") String phone) {
// 서비스로직은 아직 고려 x
String response = //(d)
"{\"" +
"email\":\""+email+"\"," +
"\"name\":\""+name+"\",\"" +
"phone\":\"" + phone+
"\"}";
return response;
}
@GetMapping("/{member-id}") //(e)
public String getMember(@PathVariable("member-id")long memberId) { //(f)
// 서비스로직은 아직 고려 x
return null;
}
@GetMapping
public String getMembers() {
// 서비스로직은 아직 고려 x
return null;
}
}
- (a) @RequestMapping(value = "/v1/members", produces = {MediaType.APPLICATION_JSON_VALUE})
- produces애트리뷰트(Attribute)는 응답 데이터를 어떤 미디어 타입으로 클라이언트에게 전송할 지를 설정
- 여기서는 JSON 형식의 응답 데이터를 전송하겠다는 의미
- 이 값을 설정하지 않으면 여기서 설정한 JSON 형식의 데이터가 아닌 문자열 자체를 전송
- (b) @PostMapping
- 클라이언트의 요청 데이터(request body)를 서버에 생성(POST)할 때 사용하는 애너테이션
- 요청 전송 시, HTTP Method 타입을 동일하게 맞춰(POST) 전송해야 한다
- (c) @RequestParam("~~")
- 핸들러 메서드의 파라미터 종류 중 하나
- 주로 클라이언트로부터 쿼리 파라미터(Query Parmeter 또는 Query String), 폼 데이터(form-data), x-www-form-urlencoded 형식으로 전송된 데이터를 받을 때 사용하는 애너테이션
- (d) String response = "~~~(JSON형식)~~~"
- REST API 기반 애플리케이션이기 때문에 JSON 형식에 맞게 작성한 후, JSON 형식의 응답데이터 전송
- 지금처럼 일일이 작성하면 번거롭고 오타로 인한 에러가 발생할 가능성이 높다 -> 개선예정
- (e) @GetMapping("/{member-id}")
- 클라이언트가 서버에 리소스를 조회(GET)할 때 사용하는 애너테이션
- 애너테이션의 괄호 안 애트리뷰트로, 전체 HTTP URI의 일부를 지정 (사용할 수 있는 애트리뷰트 더 있음)
- {member-id}는 회원 식별자를 의미하며, 클라이언트가 어떤 값을 지정하느냐에 따라 동적으로 바뀌는 값
- (f) @PathVariable("member-id")
- @RequestParam과 마찬가지로 핸들러 메서드의 파라미터 종류 중 하나
- 괄호 안의 값은 @GetMapping("/{member-id}")의 중괄호{}안의 문자열과 동일해야 한다. => “member-id”
- 만약 두 문자열이 다르다면 MissingPathVariableException이 발생한다.
3) 개선이 필요한 곳
- 수작업으로 만들어준 JSON문자열
- @RequestParam 애너테이션을 통한 요청 파라미터 수신 (파라미터가 늘어날 경우 비효율적임)
🔸 응답데이터에 ResponseEntity 적용 ("수작업으로 만들어준 JSON문자열" 개선!!)
1) 개선된 코드
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/v1/members") // (a)
public class MemberController {
@PostMapping
public ResponseEntity postMember(@RequestParam("email") String email,
@RequestParam("name") String name,
@RequestParam("phone") String phone) {
// (b)
Map<String, String> map = new HashMap<>();
map.put("email", email);
map.put("name", name);
map.put("phone", phone);
// (c)
return new ResponseEntity<>(map, HttpStatus.CREATED);
}
@GetMapping("/{member-id}")
public ResponseEntity getMember(@PathVariable("member-id") long memberId) {
System.out.println("# memberId: " + memberId);
// 서비스로직은 아직 고려 x
// (d)
return new ResponseEntity<>(HttpStatus.OK);
}
@GetMapping
public ResponseEntity getMembers() {
System.out.println("# get Members");
// 서비스로직은 아직 고려 x
// (e)
return new ResponseEntity<>(HttpStatus.OK);
}
}
- (a) 기존에 있던 produces 애트리뷰트 없애줬다
- (b)에서 보면 Map을 던져줄 건데, Map 을 던져주면 내부적으로 JSON형식의 응답데이터로 자동변환 해주기 때문이다.
- (b) JSON 문자열을 직접 작성하는 수작업을 Map객체로 대체하였다.
- (c) 리턴 시에 map과 HTTP응답상태를 ResponseEntity로 한번 감싸서 리턴해주었다.
- (d), (e)도 마찬가지로 수정해주었다.
'backend > spring' 카테고리의 다른 글
[Spring] 객체지향을 위한 스프링의 노력 - IoC / DI / 컨테이너 (1) | 2025.04.27 |
---|---|
[Spring] HTTP Header, Rest Client (0) | 2025.04.11 |
[Spring] MVC - JDBC 기반 데이터 액세스 계층 (0) | 2025.04.11 |
[Spring] MVC (0) | 2025.04.10 |