Skip to content

[REFACTOR] SolvedAc Client/Service 역할 분리 #142

@ryuwldnjs

Description

@ryuwldnjs

배경

solvedac 도메인 내 SolvedAcClientSolvedAcService의 책임 경계가 모호했습니다.


Phase 1: 역할 분리 (커밋 완료)

1. Client에 비즈니스 로직 혼재 ✅

// Before - Client에 로직 혼재
public ProblemSearchResponse searchProblems(String query, int count) {
    var limited = resp.items().stream().limit(count).toList();  // ❌
}

// After - 순수 HTTP 호출만
public ProblemSearchResponse searchProblems(String query, String sort, String direction) {
    return get("/search/problem", Map.of(...), ProblemSearchResponse.class);
}

2. 테스트 코드 혼재 ✅

  • SolvedAcService.recommendTest() 삭제
  • TestController.java 삭제

3. dead code 정리 ✅

  • SolvedAcClient.hasUserSolvedProblem() → Service로 이동
  • SolvedAcClient.getSolvedProblemsRaw() 삭제
  • SolvedAcService.fetchSolvedProblems() 삭제

Phase 2: 네이밍 + 패키지 구조 개선

패키지 이동

solvedac/는 자체 엔티티/Controller/Repository가 없는 외부 API 연동 전용 패키지이므로 infrastructure/로 이동.

# Before
solvedac/
├── api/ → client/
│   └── SolvedAcClient.java
├── dto/
└── SolvedAcService.java

# After
infrastructure/solvedac/
├── SolvedAcClient.java              # @Component, 쿼리 조합 + 예외 변환 + 응답 가공
├── client/
│   └── SolvedAcRestClient.java      # @Component, 순수 HTTP 호출 (내부 구현)
└── dto/
    ├── SolvedAcUserResponse.java
    ├── SolvedAcUserBioResponse.java
    ├── ProblemSearchResponse.java
    ├── ProblemInfo.java
    ├── SolvedAcTagInfo.java
    └── SolvedAcVerificationResponse.java

네이밍 변경

Before After 이유
SolvedAcService SolvedAcClient infrastructure 레이어에 "Service"는 부적절. Spring 생태계에서 외부 연동은 "Client" 네이밍이 표준
SolvedAcClient SolvedAcRestClient 저수준 HTTP 전용임을 명시. client/ 하위에 배치하여 외부 직접 접근 방지
BojVerificationDto SolvedAcUserBioResponse 사용처(인증) 기준이 아닌 데이터 내용(bio) 기준 네이밍. SolvedAcUserResponse와 일관성
@Service @Component infrastructure 레이어 컨벤션 통일

역할 분담

구분 SolvedAcRestClient SolvedAcClient
책임 순수 HTTP 호출 쿼리 조합, 응답 가공, 예외 처리
파라미터 API 스펙 그대로 비즈니스 요구사항
응답 원본 그대로 반환 가공/필터링/변환
예외 그대로 throw try-catch로 CustomException 변환

BojVerificationFacade 의존성 정리 ✅

// Before - RestClient 직접 호출 (역할 분리 원칙 위반)
private final SolvedAcRestClient solvedAcRestClient;
userInfo = solvedAcRestClient.getUserBio(handle);

// After - SolvedAcClient 경유 (예외 처리 일원화)
private final SolvedAcClient solvedAcClient;
userInfo = solvedAcClient.getUserBio(handle);

MemberService 필드명 통일 ✅

  • solvedacServicesolvedAcClient (카멜케이스 통일)

체크리스트

Phase 1

  • SolvedAcClient.searchProblems() - count 제한 로직 제거, 순수 HTTP 호출만
  • SolvedAcClient.hasUserSolvedProblem() 제거 → Service로 이동
  • SolvedAcClient.getSolvedProblemsRaw() 제거 (dead code)
  • SolvedAcService - 응답 가공 로직 통합
  • SolvedAcService.recommendTest() 삭제
  • SolvedAcService.fetchSolvedProblems() 삭제 (dead code)
  • TestController.java 삭제
  • 패키지명 변경: api/client/
  • 예외 처리 일관성 추가
  • 로그 레벨 조정

Phase 2

  • solvedac/infrastructure/solvedac/ 패키지 이동
  • SolvedAcServiceSolvedAcClient 리네임 (@Service@Component)
  • SolvedAcClientSolvedAcRestClient 리네임 (client/ 하위 유지)
  • BojVerificationDtoSolvedAcUserBioResponse 리네임
  • SolvedAcClientgetUserBio() 메서드 추가 (예외 처리 포함)
  • BojVerificationFacadeSolvedAcClient 경유로 변경
  • MemberService 필드명 solvedacServicesolvedAcClient 통일
  • 전체 import 경로 업데이트 (8개 파일)
  • 빌드 + 테스트 통과 확인

향후 과제 (별도 이슈)

  • ProblemInfo DTO 역할 분리: API 응답 전용 DTO와 도메인 DTO 분리 검토 (현재 1개 DTO가 API 매핑 + 서비스 간 전달 + Controller 응답 3가지 역할을 겸함)
  • Resilience4j 적용 시 SolvedAcClient에 @Retry, @CircuitBreaker 추가

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions