Spring Boot 개념 정리

Zoonmy·2025년 3월 4일

REST API

REST 란 ?

Representational State Transfer

  • Representation (표현) : json, xml …. 와 같이 자원 (Resources)을 하나의 표현식으로 나타내는 것
  • State (상태) : GET, POST, PUT, DELETE, PATCH, … 등과 같이 클라이언트와 서버 간의 자원의 상태 를 의미
  • Transfer (전송) : Rest는 HTTP Protocol 을 통해서 데이터를 전달받고, 전송한다. → 자원은 URI 로 식별된다.

    URI와 URL의 차이

    URI (Uniform Resource Identifier) - 통합 자원 식별자

    자원을 식별 하는 문자열 = 고유한 이름 = 고유한 경로
    • 2가지로 나뉠 수 있다.

      URN (Uniform Resource Name)

    • 자원의 이름을 식별 → 자원 자체를 고유로 식별

      URL (Uniform Resource Locator)

    • 자원의 위치를 식별 → 자원에 어떻게 접근할지를 설명


      URL (Uniform Resource Locator) - 통합 자원 위치

      URL은 자원의 위치를 명시하는 URI 의 일종

    • URL은 자원이 위치한 프로토콜, 호스트, 경로 등의 정보를 제공

      [Example]
      
      https://www.example.com/index.html
      
    • https는 프로토콜

    • www.example.com은 호스트명

    • /index.html은 경로

      
      → URL은 자원의 위치와 접근 방법(HTTP, FTP 등)을 함께 명시
      
      ---
      

Spring Data JPA

Dirty Checking

  • JPA가 엔티티의 변경 사항을 자동으로 감지 → 메커니즘 덕분에 개발자는 수동으로 UPDATE 쿼리를 작성할 필요 없이, 엔티티의 상태 변화 만으로 데이터베이스에 변경사항 적용 가능

작동 방식

  1. 엔티티 조회:
    • JPA를 사용하여 엔티티를 조회하면, 해당 엔티티는 영속성 컨텍스트(Persistence Context)에 저장
  2. 스냅샷 저장:
    • 조회된 엔티티의 초기 상태(값)를 스냅샷으로 영속성 컨텍스트에 저장
  3. 엔티티 변경:
    • 애플리케이션에서 엔티티의 필드 값을 변경 → update 를 어플리케이션에서 구현
  4. 트랜잭션 커밋 시점에 Dirty Checking 수행:
    • 트랜잭션이 종료되기 전 flush()가 호출될 때, JPA는 엔티티의 스냅샷과 현재 상태를 비교
    • 만약 변경된 부분이 있다면 자동으로 UPDATE 쿼리를 생성하여 데이터베이스에 반영

save() 내부 구조 보기

SimpleJpaRepository.class 에 명시된 내용을 확인하자.

@Transactional
    public <S extends T> S save(S entity) {
        Assert.notNull(entity, "Entity must not be null");
        if (this.entityInformation.isNew(entity)) {
            this.entityManager.persist(entity);
            return entity;
        } else {
            return this.entityManager.merge(entity);
        }
    }
  • @Transactional 어노테이션이 붙은 것을 볼 수 있다. → @Transactional 어노테이션이 붙으면 메서드 내에 작업이 끝날 경우, flush() 메소드를 실행한다. → 이 과정에서 변경사항이 감지되면, 데이터베이스의 레코드를 업데이트 하는 쿼리가 실행된다.

동등성과 동일성

동등성 (Equality)

  • 비교 대상이 되는 두 객체가 가진 값이 같음을 의미

동일성 (Identity)

  • 두 객체가 같은 객체임을 의미

테스트 코드 작성

Given - When - Then 패턴

  • Behavior Driven Development (행위 주도 개발) 을 통해 탄생한 테스트 접근 방식 → 테스트 코드가 ‘명세’ 역할을 할 수 있다는 점에서 장점!

F.I.R.S.T 전략

  • Fast [빠르게]
    • 목적을 단순하게 설정하거나, 외부 환경을 사용하지 않는 단위 테스트로 빠른 테스트 수행
  • Isolated [독립적]
    • 하나의 테스트 코드는 하나의 대상에 대해서만 수행.
    • 다른 테스트와 상호작용하면 안된다.
  • Repeatable [반복 가능한]
    • 어떤 환경에서도 반복 가능하도록 작성
    • 개발 환경이나 네트워크 연결 여부와 상관없이 수행되어야 한다.
  • Self-Validation [자가 검증]
    • 테스트 자체만으로 테스트 검증이 완료되어야 한다.
    • 테스트 성공 / 실패 에 대한 코드를 함께 작성해야 함
  • Timely [적시에]
    • 테스트 코드는 애플리케이션 코드 구현 전에 완성되어야 한다.

테스트 - Mock 객체

Mock 객체를 사용하는 이유?

  • 독립적인 테스트 (Isolated) 를 수행하기 위해서는, 외부 요인 없이 테스트가 수행되어야 한다. 를 만족시켜야 한다. → 따라서, 외부 요인 을 Mock 처리하여 수행

@WebMvcTest(테스트 대상 클래스.class)

  • 대상 클래스만 로드해 테스트를 수행 → 클래스를 명시하지 않으면 @Controller, @RestController, @ControllerAdvice 등의 컨트롤러 관련 빈 객체가 모두 로드됨
  • @SpringBootTest 보다 가볍게 테스트하기 위해 사용됨

@MockBean

  • 실제 빈 객체가 아닌 Mock(가짜) 객체를 생성해서 주입하는 역할을 수행 → 실제 객체가 아니기 때문에 실제 행위를 수행하지 않음
  • Mockitogiven() 메서드를 통해 동작을 정의해야 함

실행 예제

@WebMvcTest(UserController.class) // Controller 단위 테스트
public class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;  // Service 계층 Mocking

    @Test
    public void getUserById_ReturnsUser() throws Exception {
        // Given
        User user = new User(1L, "John Doe");
        Mockito.when(userService.getUserById(1L)).thenReturn(user);

        // When & Then
        mockMvc.perform(get("/users/1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name").value("John Doe"))
                .andDo(print());
       
	      // Verify         
        Mockito.verify(userService, Mockito.times(1)).getUserById(anyLong());
    }
}
  • perform() : 서버로 URL 요청을 보내는 것처럼 통신 테스트 코드 작성 가능 → 리턴값(결과값)으로 ResultActions 객체가 리턴된다.
  • andExpect() : ResultActions 객체에 대한 결과값 검증을 수행할 수 있다. → ResultMatcher 를 활용
  • andDo() : 요청과 응답의 전체 내용을 확인
  • verify() : 지정된 메서드가 실행됐는지 검증 → given()에 정의된 동작과 대응

@MockBeanMockito.mock()

사용 방법@MockBeanMockito.mock()
Spring 통합Spring 컨텍스트 내에서 Mock 객체 사용Spring과 무관하게 독립적인 단위 테스트 가능
테스트 속도상대적으로 느림빠르고 가벼움
의존성 주입자동으로 주입 (DI)직접 객체 생성 후 주입 필요
용도Spring MVC, 통합 테스트단순한 단위 테스트
추천 사용 예시@WebMvcTest, @SpringBootTest 등에서 사용독립적인 비즈니스 로직 단위 테스트 시 사용
@MockBean
private UserService userService;  // MockBean 사용

private UserService userService = Mockito.mock(UserService.class); // Mockito 사용

@DataJpaTest 어노테이션

  • Repository 단을 테스트 할 때 붙이는 어노테이션
    • JPA와 관련된 설정만 로드해서 테스트 진행
    • 기본적으로 @Transactional 이 포함되어있어, 테스트 코드가 종료되면 자동적으로 DB 롤백 → 데이터베이스에 영향을 주지 않으므로 안전한 테스트가 가능
    • 기본값으로 임베디드 데이터베이스 사용 → 별도의 설정 없이 H2 같은 인메모리 데이터베이스를 자동으로 사용
    • Repository 빈만 로드 → Repository와 관련된 빈들만 로드하여 테스트 속도가 빠름

TDD 개발 방법론

총 3개의 단계로 개발 주기를 표현

  1. 실패 테스트 작성 : 실패하는 경우의 테스트 코드를 먼저 작성
  2. 테스트를 통과하는 코드 작성 : 테스트 코드를 성공시키기 위한 실제 코드 작성
  3. 리팩토링 : 중복 코드를 제거하거나 일반화하는 리팩토링 수행

일반적인 개발 방법과 차이점

  • 일반적인 개발 방법은 설계어플리케이션 코드테스트 코드 순서
  • 테스트 주도 개발 방법은 설계테스트 코드어플리케이션 코드 순서

profile
열시미 해야쥐

0개의 댓글