Refactoring
기존에 Riot API를 호출하는 부분은 LolSearchAdapter라는 네이밍으로 아래와 같이 개발이 되어있었다.
@Service
public class LolSearchAdapter {
@Value("${lol.api.key}")
private String apiKey;
private final RestTemplate restTemplate;
@Autowired
LolSearchAdapter(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
/**
* User의 게임 목록을 가져옴
* @param matchId
* @return
*/
public MatchRecord searchMatch(String matchId) {
try {
MatchRecord result = restTemplate.getForObject(
"https://asia.api.riotgames.com/lol/match/v5/matches/" + matchId + "?api_key=" + apiKey,
MatchRecord.class
);
return result;
} catch (Exception e) {
throw new ApiException(SearchStatus.SEARCH_RESULT_NOT_FOUND);
}
}
}
그리고 위 Adapter가 BoardService에 인자로 들어가 있었는데, 이게 구조상 Framework(Infrastructure) Layer의 객체가 Service Layer의 객체에 Dependency로 들어간 셈이었다.
@Service
@RequiredArgsConstructor
public class BoardService {
private final MatchGameService matchGameService;
private final MatchUserService matchUserService;
private final LolSearchAdapter lolSearchAdapter;
public MatchGameResponse searchMatch(String gameId) {
Optional<MatchGame> savedEntity = matchGameService.findByGameId(gameId);
MatchGame matchGame;
if (savedEntity != null && savedEntity.isPresent()) {
matchGame = savedEntity.get();
} else {
MatchRecord vo = lolSearchAdapter.searchMatch(gameId);
matchGame = matchGameService.save(vo);
matchUserService.saveAll(vo.info().participants(), matchGame);
}
MatchGameResponse response = MatchGameResponse.toResponse(matchGame);
return response;
}
}
개발하는 내내 이 부분이 뭔가 이상했다. 지금 의존성의 방향이 안쪽에서 바깥쪽으로 향하고 있는 것이다. 외부에 있는 Layer에 절대 의존하면 안된다는게 헥사고날 아키텍처의 핵심이라고 이해했는데 말이다. 그래서 이를 우회하기 위한 수단으로 Dependency Inversion을 생각했다.
Interface를 하나두고 그 Service Layer는 Interface에 의존하게끔 한 다음 세부 구현을 모르게 한다면 LolSearchAdapter가 내부적으로 무슨 동작을 하게 되더라도 BoardService에서는 검색의 결과가 반드시 MatchRecord 라는 VO로 반환된다.
라는 사실을 알게 되는 것이다. 그래서 LolSearchAdapter가 앞으로 크롤링을 해서 긁어오던 발로란트 데이터를 긁어오던 결국은 MatchRecordVO를 가져오게 된다.
따라서 아래와 같이 Refactoring을 진행해주었다.
👇 application/ports/output/RiotApiPort
public interface RiotApiPort {
public MatchRecord searchMatch(String matchId);
}
👇 LolSearchAdapter -> RiotApiAdapter
@Service
public class RiotApiAdapter implements RiotApiPort {
}
BoardService에서는 Interface를 DI 받는 것으로 변경
public class BoardService {
private final MatchGameService matchGameService;
private final MatchUserService matchUserService;
private final RiotApiAdapter riotApi;
}
리팩터링 후 의존성 변화
번외
해당하는 리팩터링에 대해서 물어보다가 yagni
에 대해서 알게 되었다.
Yagni : You ain't gonna need it의 첫 글자를 따서 만든 약어로 오버엔지니어링을 경계하라는 말인 것 같다.
yagni를 지키지 않으면 야근이라고 외우기로 했다.
'Spring > Error & Review' 카테고리의 다른 글
[겜문철] Unchecked Exception (0) | 2024.04.20 |
---|---|
QueryDSL Join Error 해결 (0) | 2024.03.30 |
[겜문철] 개발 Log (0) | 2024.03.23 |
통합 테스트의 필요성 (0) | 2024.03.20 |
회원가입 로직 역할과 책임에 맞게 리팩터링 (0) | 2024.03.19 |