Post

[KT AIVLE] Spring 6일차 - 심화학습

0. 개요


오늘은 Spring Boot 마지막 날로 총 정리 및 실습을 위주로 진행되었습니다.


1. Spring Boot


게시물 수정 및 삭제

  • 게시물 수정
    • 특정 게시물 수정:
    1
    2
    3
    4
    5
    6
    7
    8
    
    @PostMapping("/news/update")
    public String updateNews(@RequestParam Long id, NewsDto.Post post) {
        News news = newsRepository.findById(id).orElseThrow();
        news.setTitle(post.getTitle());
        news.setContent(post.getContent());
        newsRepository.save(news);
        return "redirect:/news";
    }
    
  • 게시물 삭제
    • 특정 ID로 게시물 삭제:
    1
    2
    3
    4
    5
    
    @PostMapping("/news/delete")
    public String deleteNews(@RequestParam Long id) {
        newsRepository.deleteById(id);
        return "redirect:/news";
    }
    

에러 처리

  • 에러 처리 방식
    • 스프링 기본 Whitelabel Error 페이지:
    • 기본적으로 처리되지 않은 예외가 발생하면 Spring Boot의 Whitelabel Error 페이지가 표시됨.
    • 에러 페이지를 커스터마이징하려면 설정 필요.
  • 에러 처리 예제
    • ControllerAdvice를 활용하여 전역 에러 처리:
    1
    2
    3
    4
    5
    6
    7
    8
    
    @ControllerAdvice
    public class GlobalExceptionHandler {
        @ExceptionHandler(Exception.class)
        public String handleException(Exception e, Model model) {
            model.addAttribute("error", e.getMessage());
            return "error";
        }
    }
    
  • error.mustache 뷰:

    1
    2
    
    <h1>에러 발생</h1>
    <p></p>
    
  • HTTP 상태별 에러 처리
    • application.properties에 설정:
      • properties
      1
      2
      
      server.error.whitelabel.enabled=false
      server.error.include-message=always
      


Reset API


1: REST / REST API

  • REST란?

    • REST는 Representational State Transfer의 약자로, 자원을 이름으로 구분하여 해당 자원의 상태를 주고받는 방식입니다.
    • 2000년 로이 필딩의 논문에서 처음 소개되었으며, HTTP 프로토콜의 우수성을 극대화한 웹 아키텍처입니다.
  • REST의 주요 개념
    • 자원(Resource): 소프트웨어가 관리하는 모든 것 (이미지, 데이터, 문서 등).
    • 표현(Representation): 자원을 표현하기 위한 데이터 형식 (JSON, XML 등).
    • 상태 전송(State Transfer): 요청 시 자원의 상태를 전달.
  • REST의 특징
    • 인터페이스 일관성: GET, POST, PUT, DELETE 등의 HTTP 메서드를 활용하여 작업 수행.
    • 무상태성(Stateless): 클라이언트 요청에 대한 상태를 서버에서 저장하지 않음.
    • 캐싱(Cacheable): HTTP 캐싱 기술 활용 가능.
    • 서버-클라이언트 분리: 클라이언트와 서버의 책임을 명확히 분리.
    • 계층형 구조: 로드 밸런싱, 프록시 등을 활용하여 확장성 제공.
  • REST API 설계 예시
    • URI는 명사를 사용하고, 소문자를 권장.
    • 예: GET /users, POST /books
    • 파일 확장자는 포함하지 않음.

2: REST API 요청/응답 과정

  • HTTP 요청 구성

    • 요청 메시지는 시작 라인, 헤더, 본문으로 구성됩니다. ```bash POST /news HTTP/1.1 Host: api.example.com Content-Type: application/json

    { “title”: “첫 번째 뉴스”, “content”: “JSON 데이터 형식 알아보기” } ```

  • HTTP 응답 구성
    • 응답 메시지에는 상태 라인, 헤더, 본문이 포함됩니다.
    • 상태 코드 예시:
      • 200 OK: 요청 성공.
      • 404 Not Found: 요청한 리소스를 찾을 수 없음.
  • JSON 데이터 형식
    • JSON은 키-값 쌍으로 구성된 텍스트 기반의 데이터 형식.
    • 배열은 대괄호([]), 객체는 중괄호({})로 표현.
      1
      2
      3
      4
      5
      
      {
        "newsId": 1,
        "title": "첫 번째 뉴스",
        "content": "JSON 형식 이해하기"
      }
      

3: REST API 구현

  • 도서 관리 API 설계

  • API 기능:
    • 도서 추가: POST /books
    • 도서 수정: PUT /books/{id}
    • 도서 삭제: DELETE /books/{id}
    • 도서 조회: GET /books
    • API 테스트 코드 포함.
  • 프로젝트 설정
    • Spring Boot 프로젝트 생성:
    • 의존성: Lombok, Spring Web, Spring Data JPA, H2 Database.
    • application.yml:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      
      spring:
      datasource:
        url: jdbc:h2:~/library
        username: sa
        password: 1234
      jpa:
        hibernate:
          ddl-auto: create
        show-sql: true
      
  • 엔티티 및 DTO 설
    • Book 엔티티:
      1
      2
      3
      4
      5
      6
      7
      
      @Entity
      public class Book {
        @Id @GeneratedValue
        private Long id;
        @Column(nullable = false) private String title;
        @Enumerated(EnumType.STRING) private Status status;
      }
      
    • BookDTO: 데이터를 전달하고 유효성 검사를 포함.
      1
      2
      3
      
      public static class Post {
        @NotBlank private String title;
      }
      
  • PUT과 PATCH의 차이점
    • PUT: 전체 필드를 업데이트.
    • PATCH: 특정 필드만 업데이트.

4: Service Layer

  • 서비스 계층의 역할
    • 비즈니스 로직 처리.
    • 데이터 액세스 계층과의 상호작용.
    • BookService 구현
  • CRUD 로직 처리:
    1
    2
    3
    4
    
    public Book insertBook(BookDTO.Post dto) {
      Book book = mapper.PostDTOToEntity(dto);
      return bookRepository.save(book);
    }
    
  • 트랜잭션 관리
    • @Transactional:
      • 트랜잭션의 원자성, 일관성, 고립성, 지속성을 보장.

5: @Transactional

  • 트랜잭션의 주요 속성
    • Propagation: 기존 트랜잭션에 참여하거나 새로 생성.
    • Isolation: 트랜잭션 간 간섭 방지.
    • ReadOnly: 읽기 전용 트랜잭션.
    • 트랜잭션 설정
  • Spring에서 트랜잭션 관리 설정:
    1
    2
    3
    4
    5
    6
    7
    
    @EnableTransactionManagement
    public class AppConfig {
      @Bean
      public PlatformTransactionManager transactionManager() {
          return new JpaTransactionManager();
      }
    }
    

6: 컨트롤러 구현

  • RestController 소개
    • @RestController:
    • JSON 또는 XML 형식의 데이터를 반환하는 컨트롤러를 생성할 때 사용.
    • @Controller와 @ResponseBody의 결합 형태.
  • 컨트롤러 설계
  • 도서 관리 API의 컨트롤러 작성:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
      @RestController
      @RequestMapping("/books")
      public class BookController {
      private final BookService bookService;
    
      public BookController(BookService bookService) {
          this.bookService = bookService;
      }
    
      @PostMapping
      public ResponseEntity<Book> createBook(@RequestBody BookDTO.Post dto) {
          Book savedBook = bookService.insertBook(dto);
          return ResponseEntity.status(HttpStatus.CREATED).body(savedBook);
      }
      }
    
  • HTTP 메서드 처리
    • 각 메서드에 적합한 HTTP 메서드 매핑:
    • POST: 자원 생성.
    • GET: 자원 조회.
    • PUT: 자원 전체 업데이트.
    • DELETE: 자원 삭제.
  • URL 설계
    • URL의 명확한 구조와 RESTful 원칙 준수:
    • 예: /books/{id}, /books?page=1.

7: 예외 처리

  • 예외 처리 전략
    • 스프링 기본 예외 처리
      • Spring Boot는 기본적으로 Whitelabel Error 페이지를 제공.
      • JSON 형식 응답으로 변경 가능.
      • 커스텀 예외 처리
    • 특정 상황에 따라 커스텀 예외를 정의:
      1
      2
      3
      4
      5
      
      public class BookNotFoundException extends RuntimeException {
        public BookNotFoundException(Long id) {
            super("Book not found with id: " + id);
        }
      }
      
  • 전역 예외 처리
    • @ControllerAdvice 사용:
      1
      2
      3
      4
      5
      6
      7
      
      @ControllerAdvice
      public class GlobalExceptionHandler {
        @ExceptionHandler(BookNotFoundException.class)
        public ResponseEntity<String> handleBookNotFound(BookNotFoundException e) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage());
        }
      }
      
  • 응답 형식
    • 예외 발생 시 JSON 형식 응답 예제:
      1
      2
      3
      4
      5
      6
      7
      
      {
        "timestamp": "2024-01-01T12:00:00",
        "status": 404,
        "error": "Not Found",
        "message": "Book not found with id: 1",
        "path": "/books/1"
      }
      

8: 페이징과 정렬

  • 페이징 처리
    • Spring Data JPA에서 제공하는 Pageable 활용:
      1
      2
      3
      4
      5
      
      @GetMapping
      public ResponseEntity<Page<Book>> getAllBooks(Pageable pageable) {
        Page<Book> books = bookService.getAllBooks(pageable);
        return ResponseEntity.ok(books);
      }
      
  • 정렬 처리
    • Pageable 객체에 정렬 조건 추가:
      1
      
      Pageable pageable = PageRequest.of(0, 10, Sort.by("title").ascending());
      
  • 응답 형식
    • 페이징된 데이터를 반환할 때 JSON 응답 예제:
      1
      2
      3
      4
      5
      6
      7
      8
      
      {
        "content": [
            { "id": 1, "title": "Spring Boot" },
            { "id": 2, "title": "Java Programming" }
        ],
        "pageable": { "pageNumber": 0, "pageSize": 10 },
        "totalPages": 5
      }
      

9: HATEOAS

  • HATEOAS란?
    • HATEOAS(Hypermedia As The Engine Of Application State):
    • REST API의 확장 개념으로, 응답에 링크를 포함하여 클라이언트가 가능한 작업을 안내.
    • 예: /books/1의 응답에 다음 링크 추가:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      
      {
        "id": 1,
        "title": "Spring Boot",
        "_links": {
            "self": { "href": "/books/1" },
            "update": { "href": "/books/1" },
            "delete": { "href": "/books/1" }
        }
      }
      
  • Spring HATEOAS 적용
    • Spring Boot에서 HATEOAS 사용:
      1
      2
      3
      4
      5
      6
      7
      8
      
      @GetMapping("/{id}")
      public EntityModel<Book> getBook(@PathVariable Long id) {
        Book book = bookService.getBookById(id);
        return EntityModel.of(book,
            linkTo(methodOn(BookController.class).getBook(id)).withSelfRel(),
            linkTo(methodOn(BookController.class).deleteBook(id)).withRel("delete")
        );
      }
      

10: 테스트

  • 단위 테스트
    • JUnit과 Mockito를 사용하여 서비스 계층 테스트:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      
      @ExtendWith(MockitoExtension.class)
      public class BookServiceTest {
        @Mock private BookRepository bookRepository;
        @InjectMocks private BookService bookService;
      
        @Test
        public void testInsertBook() {
            Book book = new Book(null, "Spring Boot", Status.AVAILABLE);
            when(bookRepository.save(any(Book.class))).thenReturn(book);
            Book savedBook = bookService.insertBook(new BookDTO.Post("Spring Boot"));
            assertEquals("Spring Boot", savedBook.getTitle());
        }
      }
      
  • 통합 테스트
    • MockMvc를 활용하여 컨트롤러 계층 테스트:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      
      @WebMvcTest(BookController.class)
      public class BookControllerTest {
        @Autowired private MockMvc mockMvc;
      
        @Test
        public void testGetAllBooks() throws Exception {
            mockMvc.perform(get("/books"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.content").isArray());
        }
      }
      
  • 테스트 데이터 설정
    • @Sql 어노테이션을 활용하여 초기 데이터를 설정:
      1
      
      @Sql(scripts = "/test-data.sql")
      


3. 후기


길고 길었던 Spring 강의가 끝났습니다.

이번 강의를 끝으로 KT Aivle 강의가 종료되었습니다.

앞으로 남은 일정은 마지막 미니프로젝트와 빅프로젝트입니다!

남은 기간도 열심히 달려보도록 하겠습니다!

This post is licensed under CC BY 4.0 by the author.