학습한 내용
스프링 부트 서비스 계층 리팩토링과 테스트 코드 작성에 대해 학습
1. 서비스 계층의 인터페이스 분리
•
인터페이스 정의: TestService 인터페이스를 생성하여 서비스 계층의 추상화 구현
•
구현체 분리: TestServiceImpl 클래스에서 실제 비즈니스 로직 구현
•
의존성 역전 원칙: 상위 모듈이 하위 모듈에 의존하지 않도록 인터페이스 활용
2. 서비스 메서드별 역할
•
getAllMembers(): 전체 회원 목록 조회
•
saveMember(): 회원 정보 저장 (유효성 검증 포함)
•
getMemberById(): ID로 특정 회원 조회
•
deleteMember(): ID로 회원 삭제 (두 가지 방식 구현)
3. 입력값 검증 및 예외 처리
•
StringUtils.hasText(): 문자열 유효성 검증 유틸리티 활용
•
Optional 패턴: orElse(null)을 통한 안전한 데이터 조회
•
존재성 확인: existsById()와 findById()를 활용한 삭제 방식
4. 스프링 부트 테스트 프레임워크
•
@SpringBootTest: 전체 애플리케이션 컨텍스트를 로드하는 통합 테스트
•
@AutoConfigureMockMvc: MockMvc 자동 구성으로 웹 계층 테스트 환경 설정
•
MockMvc: HTTP 요청을 시뮬레이션하여 컨트롤러 테스트 수행
5. 테스트 생명주기 관리
•
@BeforeEach: 각 테스트 메서드 실행 전 초기화 작업
•
@AfterEach: 각 테스트 메서드 실행 후 데이터베이스 초기화
•
@DisplayName: 테스트 메서드에 의미있는 한글 설명 추가
6. HTTP 메서드별 테스트 패턴
•
GET/POST/DELETE: mockMvc.perform()으로 각 HTTP 메서드 테스트
•
응답 검증: jsonPath()를 활용한 JSON 응답 내용 검증
•
Given-When-Then 패턴: 테스트 시나리오를 명확하게 구조화
실습코드
// 서비스 인터페이스
public interface TestService {
List<Member> getAllMembers();
Member saveMember(Member member);
Member getMemberById(Long id);
boolean deleteMember(Long id);
}
// 서비스 구현체
@Service
public class TestServiceImpl implements TestService {
final MemberRepository memberRepository;
@Override
public Member saveMember(Member member) {
String name = member.getName() == null ? "" : member.getName();
if(!StringUtils.hasText(name)){
return null;
}
member.setName(name);
return memberRepository.save(member);
}
@Override
public boolean deleteMember(Long id) {
if (memberRepository.existsById(id)) {
memberRepository.deleteById(id);
return true;
} else {
return false;
}
}
}
// 컨트롤러 테스트
@SpringBootTest
@AutoConfigureMockMvc
class TestControllerTest {
@Autowired
protected MockMvc mockMvc;
@DisplayName("전부 가지고 오는 api 테스트")
@Test
void getAllMembers() throws Exception {
// given
final String url = "/members";
Member saved = memberRepository.save(new Member(1L, "seo"));
// when
final ResultActions result = mockMvc.perform(
get(url).accept(MediaType.APPLICATION_JSON));
// then - 검증
result.andExpected(status().isOk())
.andExpect(jsonPath("$[0].id").value(saved.getId()));
}
}
TypeScript
복사
1.
인터페이스 기반 서비스 설계
•
추상화 구현: TestService 인터페이스로 서비스 계층의 공통 메서드 정의
•
생성자 주입: final 키워드와 함께 생성자 주입으로 의존성 불변성 보장
2.
입력값 검증 로직
•
null 안전성: member.getName() == null 체크로 NullPointerException 방지
•
문자열 검증: StringUtils.hasText()로 빈 문자열과 공백 문자열 모두 검증
3.
삭제 메서드 구현
•
existsById() 방식: 존재성을 먼저 확인한 후 삭제 수행으로 효율적인 조회
•
boolean 반환: 삭제 성공/실패 여부를 명확하게 반환
4.
테스트 환경 및 검증
•
통합 테스트: @SpringBootTest로 실제 애플리케이션 컨텍스트 로드
•
Given-When-Then: 테스트 시나리오를 명확한 3단계로 구분
•
JSON 응답 검증: jsonPath()를 활용하여 응답 JSON의 특정 필드값 검증