Ch8. 목록 조회 API & Swagger

김지민·2024년 12월 29일

UMC springboot

목록 보기
7/9

목표

  1. Database와 상호작용하는 API의 전체 과정을 익힌다.
  2. Swagger 세팅을 학습한다.
  3. 어노테이션을 활용한 데이터 검증 방법을 익힌다.

8주차 주제 및 진행

주요 내용은 Database와 상호작용하는 API의 흐름을 이해하는 것입니다.
지난 포스팅의 내용을 기반으로 구체적인 예제를 다룹니다.

또한, Service와 Controller 단에서 데이터 검증 처리를 코드가 아닌 어노테이션으로 수행하는 방법을 통해 단일 책임 원칙을 준수하고, 더 깔끔한 코드 작성 방법을 배웁니다.


1. 회원 가입 API 구현

GitHub 이슈 및 브랜치 만들기
1. GitHub에서 이슈를 생성합니다.
라벨은 깃모지(gitmoji)를 활용해 꾸며보세요.
예시:
- 🐛 :bug:
- 📝 :memo:
- ✨ :sparkles:
2. 브랜치 생성
- 브랜치 이름 예시: feature/#이슈번호
- 예를 들어, feature/#3
3. 브랜치 작업 시 주의사항
- 항상 작업할 브랜치가 최신 상태인지 확인 후 작업을 시작하세요.

1) DTO 생성

회원 가입에 필요한 Request와 Response DTO를 생성합니다.

// Request DTO
public class MemberRequestDTO {
    @Getter
    public static class JoinDto {
        String name;
        Integer gender;
        Integer birthYear;
        Integer birthMonth;
        Integer birthDay;
        String address;
        String specAddress;
        List<Long> preferCategory;
    }
}

// Response DTO
public class MemberResponseDTO {
    @Builder
    @Getter
    @NoArgsConstructor
    @AllArgsConstructor
    public static class JoinResultDTO {
        Long memberId;
        LocalDateTime createdAt;
    }
}

2) Converter 생성

엔티티와 DTO 간 변환을 위한 Converter를 생성합니다.

public class MemberConverter {
    public static MemberResponseDTO.JoinResultDTO toJoinResultDTO(Member member) {
        return MemberResponseDTO.JoinResultDTO.builder()
            .memberId(member.getId())
            .createdAt(LocalDateTime.now())
            .build();
    }

    public static Member toMember(MemberRequestDTO.JoinDto request) {
        Gender gender = switch (request.getGender()) {
            case 1 -> Gender.MALE;
            case 2 -> Gender.FEMALE;
            default -> Gender.NONE;
        };

        return Member.builder()
            .name(request.getName())
            .gender(gender)
            .address(request.getAddress())
            .specAddress(request.getSpecAddress())
            .build();
    }
}

3) Controller 생성

클라이언트 요청을 처리하는 Controller를 작성합니다.

@RestController
@RequiredArgsConstructor
@RequestMapping("/members")
public class MemberRestController {

    private final MemberCommandService memberCommandService;

    @PostMapping("/")
    public ApiResponse<MemberResponseDTO.JoinResultDTO> join(@RequestBody @Valid MemberRequestDTO.JoinDto request) {
        Member member = memberCommandService.joinMember(request);
        return ApiResponse.onSuccess(MemberConverter.toJoinResultDTO(member));
    }
}

4) Service 생성

비즈니스 로직을 처리하는 Service를 작성합니다.

@Service
@RequiredArgsConstructor
public class MemberCommandServiceImpl implements MemberCommandService {
    private final MemberRepository memberRepository;
    private final FoodCategoryRepository foodCategoryRepository;

    @Transactional
    @Override
    public Member joinMember(MemberRequestDTO.JoinDto request) {
        Member newMember = MemberConverter.toMember(request);

        // 선호 카테고리 처리
        List<FoodCategory> foodCategoryList = request.getPreferCategory().stream()
            .map(categoryId -> foodCategoryRepository.findById(categoryId)
                .orElseThrow(() -> new IllegalArgumentException("카테고리 없음")))
            .toList();

        foodCategoryList.forEach(category -> {
            MemberPrefer prefer = MemberPrefer.builder()
                .member(newMember)
                .foodCategory(category)
                .build();
            newMember.getMemberPreferList().add(prefer);
        });

        return memberRepository.save(newMember);
    }
}

5) Repository 생성

Spring Data JPA를 사용해 Repository를 작성합니다.

public interface MemberRepository extends JpaRepository<Member, Long> {}
public interface FoodCategoryRepository extends JpaRepository<FoodCategory, Long> {}

6) Swagger 세팅

  1. build.gradle에 의존성 추가:

    implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'
  2. Swagger 설정 클래스 생성:

    @Configuration
    public class SwaggerConfig {
        @Bean
        public OpenAPI umcStudyAPI() {
            return new OpenAPI()
                .addServersItem(new Server().url("/"))
                .info(new Info().title("UMC API").version("1.0.0").description("API 명세서"));
        }
    }
  3. Swagger 확인:
    http://localhost:8080/swagger-ui/index.html#/로 접속합니다.


7) 어노테이션 기반 Validation

  1. Request DTO에 어노테이션 추가:

    public class MemberRequestDTO {
        @Getter
        public static class JoinDto {
            @NotBlank
            String name;
            @NotNull
            Integer gender;
            @Size(min = 5, max = 12)
            String address;
            @ExistCategories
            List<Long> preferCategory;
        }
    }
  2. 커스텀 어노테이션 정의:

    @Documented
    @Constraint(validatedBy = CategoriesExistValidator.class)
    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ExistCategories {
        String message() default "카테고리 존재하지 않음";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
    }
  3. Validator 작성:

    @Component
    @RequiredArgsConstructor
    public class CategoriesExistValidator implements ConstraintValidator<ExistCategories, List<Long>> {
        private final FoodCategoryRepository foodCategoryRepository;
    
        @Override
        public boolean isValid(List<Long> value, ConstraintValidatorContext context) {
            return value.stream().allMatch(foodCategoryRepository::existsById);
        }
    }

그러면 아래 과정을 통해 완성이 되게 됩니다.


결론 및 회고

이번 주 학습에서는 Database와 상호작용하는 API의 기본 흐름을 학습했습니다. 특히 Swagger와 Validation 어노테이션을 통해 개발자 경험(DX)을 개선하는 방법을 배우는 것이 핵심이었습니다.
다음 포스팅에서는 목록 조회가 아닌 API를 만들어보면서, 페이징을 적용해보겠습니다!

profile
열혈개발자~!!

0개의 댓글