sugar.kim

Spring boot Rest API (GET/POST/PUT/DELETE) 본문

DEV/Java + Spring Boot

Spring boot Rest API (GET/POST/PUT/DELETE)

김슈가 2025. 5. 19. 22:47
728x90
반응형

Spring Boot에서 REST API를 설계할 때 흔히 사용하는 Controller‑Service‑Repository 구조(MVC 패턴 변형)에서 “Model”은 크게 두 가지 관점으로 이해 할 수 있다.

  1. 도메인 모델(Domain Model) / 엔티티(Entity)
  2. 데이터 전송 객체(Data Transfer Object, DTO) ⬅️ 이거 관련해서만 해당 내용 정리

아래에서 각각 어떻게 쓰이고, Controller에서는 어떻게 활용되는지 예시와 함꼐 살펴보자.


1. 도메인 모델 (Entity)

  • 역할: 데이터베이스 테이블 구조를 Java 클래스로 표현
  • 어노테이션: @Entity, @Table, @Id 등 JPA 표준 어노테이션 사용
  • 예시: 사용자(User) 정보를 담는 엔티티
@Entity
@Table(name = "users")
public class User {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 50)
    private String username;

    @Column(nullable = false)
    private String email;

    // Getter / Setter, 기본 생성자
}
  • Repository 레이어(@Repository)에서 이 엔티티를 CRUD 대상으로 사용
  • DB 스키마 변경 시 모델만 수정하면 된다.

2. DTO (Data Transfer Object) ⬅️ 이거 관련

  • 역할: 네트워크(HTTP) 레이어와 도메인 모델 사이에서 주고받을 데이터 형태를 정의
  • 장점
    • 엔티티에 민감 정보(비밀번호, 내부 ID 등)가 노출되는 걸 방지
    • 입력값 검증(@Valid + @NotNull 등)과 API 스펙 분리가 쉬워짐
    • API 버전 관리, 응답 필드 제어 등에 유리
// 요청 바디로 받을 때
public class UserCreateRequest {
    @NotBlank
    private String username;

    @Email @NotBlank
    private String email;

    // Getter / Setter
}

// 응답으로 반환할 때
public class UserResponse {
    private Long id;
    private String username;
    private String email;

    // 생성자, Getter
}

3. Controller 레이어 (@RestController)⬅️ 이거 관련

  • 역할: HTTP 요청을 받고, Service를 호출한 뒤, DTO로 응답 반환
  • 어노테이션
    • @RestController : @Controller + @ResponseBody
    • @RequestMapping / @GetMapping, @PostMapping 등
@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;

    // 생성자 주입
    public UserController(UserService userService) {
        this.userService = userService;
    }

    // 1) 회원 생성
    @PostMapping
    public ResponseEntity<UserResponse> createUser(
            @Valid @RequestBody UserCreateRequest req) {

        User newUser = userService.createUser(req);
        UserResponse res = new UserResponse(newUser.getId(),
                                            newUser.getUsername(),
                                            newUser.getEmail());

        return ResponseEntity
                .status(HttpStatus.CREATED)
                .body(res);
    }

    // 2) 회원 조회
    @GetMapping("/{id}")
    public ResponseEntity<UserResponse> getUser(
            @PathVariable Long id) {

        User user = userService.findById(id);
        UserResponse res = new UserResponse(user.getId(),
                                            user.getUsername(),
                                            user.getEmail());
        return ResponseEntity.ok(res);
    }

    // 기타 업데이트, 삭제 API…
}

주요 포인트

  1. 입력 검증
    • @Valid + DTO에 정의한 제약 애너테이션(@NotBlank, @Email 등)
    • @ExceptionHandler 혹은 전역 @ControllerAdvice로 에러 응답 핸들링
  2. Service 호출
    • 비즈니스 로직은 Controller가 아니라 Service 레이어에 위임
    • Controller는 순수하게 “요청 → DTO 변환 → Service 호출 → DTO 응답” 흐름만 관리
  3. 응답 상태 코드
    • 생성 시 201 CREATED, 조회 시 200 OK, 삭제 시 204 NO CONTENT 등 REST 표준에 맞춰 설정

4. Service & Repository

  • Service (@Service)
    • 트랜잭션 관리(@Transactional)
    • 도메인 모델 생성·조회·수정·삭제 비즈니스 로직
@Service
public class UserService {
    private final UserRepository repo;

    public UserService(UserRepository repo) {
        this.repo = repo;
    }

    @Transactional
    public User createUser(UserCreateRequest req) {
        User user = new User();
        user.setUsername(req.getUsername());
        user.setEmail(req.getEmail());
        return repo.save(user);
    }

    public User findById(Long id) {
        return repo.findById(id)
                   .orElseThrow(() -> new EntityNotFoundException("User not found"));
    }
}
  • Repository (@Repository, Spring Data JPA)
    • 기본 CRUD 메서드 제공: save(), findById(), deleteById() 등
public interface UserRepository extends JpaRepository<User, Long> {
    // 필요 시 추가 쿼리 메서드 정의 가능
    Optional<User> findByUsername(String username);
}

전체 요청 흐름

HTTP 요청 → DispatcherServlet
   → HandlerMapping: Controller 클래스와 메서드 결정
      → UserController
         → DTO 바인딩 & 검증
            → UserService 호출
               → UserRepository (DB) 작업
            ← 결과 리턴
         → DTO 변환
   ← ResponseEntity 반환 → HTTP 응답

 


결론

  • Model 은 크게 엔티티(Entity)DTO 로 나뉜다.
  • Controller 에서는 DTO 를 통해 요청/응답을 처리하고, Service 에서는 Entity 를 다루는 것이 모범 사례.
  • 이렇게 계층을 분리하면 유지보수성, 보안, 테스트 용이성이 모두 향상.

추가로 ModelMapper, MapStruct 같은 라이브러리를 사용해 DTO ↔ Entity 변환을 자동화할 수도 있다.


‼️Controller 에서는 DTO 를 통해 요청/응답을 처리 

📌 Spring Boot REST API 관련 어노테이션 정리

어노테이션 및 예시 설명 주요 특징 / 비고
@Slf4j 롬복(Lombok)에서 제공하는 로깅 어노테이션. log.info(), log.error() 사용 가능 System.out.println() 대신 사용해야 하는 이유:
로그 레벨 관리 가능
성능과 모니터링 최적화
운영 환경에서 로그 파일 기록 분석 가능
@RestController 컨트롤러 클래스임을 선언 + 모든 메서드가 @ResponseBody 자동 포함 주로 REST API 컨트롤러에 사용. HTML 뷰가 아닌 JSON 형태의 응답을 반환함
@RequestMapping("/api") 공통 URL Prefix 설정 base URL: http://localhost:8080/api
@GetMapping("/hello") GET 방식으로 요청 받는 메서드 매핑 예: GET http://localhost:8080/api/hello
@GetMapping("/echo/{message}") URL 경로의 일부를 변수로 받을 사용 @PathVariable String message바인딩
예: GET http://localhost:8080/api/echo/hi
@PathVariable(name="message") String msg PathVariable 명이 변수명과 다를 매핑 http://.../echo/hellomsg = "hello"
@GetMapping("/book")
@RequestParam String category
쿼리 파라미터를 1:1받아올 사용 예: GET /book?category=IT&issuedYear=2023
@RequestParam(name="issued-month") String issuedMonth 쿼리파라미터 이름과 변수명이 다를 명시적으로 이름 설정 ?issued-month=01issuedMonth = "01"
BookQueryParam bookQueryParam 쿼리파라미터가 여러 개인 경우 DTO한번에 바인딩 가능 DTO 클래스는 레퍼런스 타입(String 등) 으로 구성해야 함.
📌 이유: 기본형(int, boolean)null 처리가 안돼서 값이 누락되면 예외 발생
DTO 어노테이션
@Data, @AllArgsConstructor, @NoArgsConstructor
Lombok에서 DTO 클래스의 필수 메서드 자동 생성 @Data: Getter, Setter, toString
@AllArgsConstructor: 전체 필드 있는 생성자
@NoArgsConstructor: 파라미터 없는 기본 생성자
@PostMapping("/post")
@RequestBody
JSON 형태의 요청 본문(body)자바 객체로 바인딩 주로 DTO 사용: @RequestBody UserDto dto
@PutMapping("/put")
@RequestBody
PUT 방식 (수정 요청) + 바디로 데이터 전달 PUTJSON body → DTO 매핑
@DeleteMapping({"/user/{userName}/delete", "/user/{userName}/del"}) 2이상의 경로를 하나의 메서드로 처리 가능 @PathVariable String userName으로 매핑 가능
 

DTO레퍼런스 타입(String 등)으로 작성해야 하나?

구분 설명
기본형 사용 예시 private int age;
문제점 쿼리 파라미터에서 age들어올 경우 → intnull불가능 → 400 오류 발생
레퍼런스형 사용 예시 private Integer age;, private String issuedYear;
장점 누락되면 null들어와도 예외 발생 함 → 검증 단계에서 처리 가능
안정적 API 처리 가능
 

결론:
DTO에서는 intInteger, booleanBoolean, doubleDouble 처럼 레퍼런스 타입으로 선언해야 합니다.


System.out.println() 대신 log.info()써야 하는 이유

구분 System.out.println() og.info() <from @Slf4j>
용도 단순 출력 로그 수준별 출력 로깅 시스템 연동
로그 레벨 없음 trace, debug, info, warn, error
성능 느림, 비효율적 로그 출력 여부에 따라 내부적으로 출력 여부 결정되어 효율적
운영 환경 로그파일 저장 불가 로그 파일로 저장 가능, 로그 관리 시스템과 연동 가능
필터링 로그레벨, 태그, 모듈별 필터링 가능
 

결론:
운영 서비스에서는 반드시 System.out 대신 log.info(), log.error() 등으로 로그를 남겨야 하며,
이때 @Slf4j클래스에 붙이면 별도 선언 없이 바로 log 객체를 사용할 있어 편리합니다.

📌 Jackson JSON 네이밍 전략 관련 어노테이션

어노테이션 설명 예시 및 비고
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) JSON 요청/응답의 필드명을 **스네이크 케이스(snake_case)**매핑 클래스 단위로 사용
DTO 필드가 issuedYear경우
JSON 키는 issued_year매핑됨

예)
{"issued_year": "2023"}dto.getIssuedYear() 접근 가능
 

언제 사용하는가?

  • 클라이언트(JSON)가 **스네이크 케이스(snake_case)**사용하는 경우
  • 서버 자바 코드는 **카멜 케이스(camelCase)**유지하면서 매핑을 맞추고 싶을

사용 위치 예시

@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class BookQueryParam {
    private String issuedYear;      // → JSON: issued_year
    private String issuedMonth;     // → JSON: issued_month
    private String issuedDay;       // → JSON: issued_day
}
 

 

728x90
반응형

'DEV > Java + Spring Boot' 카테고리의 다른 글

REST API란? (+상세 설명 까지)  (0) 2025.05.19
Comments