일상/Kernel360

[Kernel360] 생산성과 리팩토링에 대한 고찰

Emil :) 2024. 4. 8. 11:54
728x90
반응형

서론


Kernel360에서 파이널 프로젝트로 클라이밍 커뮤니티 앱, 오루리 프로젝트를 진행 중입니다.

저희 팀을 포함한 총 5개의 팀이 4개월이라는 긴 프로젝트 기간의 마무리 단계에 있는데요.
프로젝트를 진행할수록 생산성과 코드의 품질, 생산성을 향상시키는 리팩토링에 대해 정말 많은 고민을 했던 것 같습니다.
특정 로직에 대해 리팩토링을 할지.. 갈아엎을지.. 그렇게 하기엔 너무 볼륨이 커서 묻어두고 다른 작업을 할지..

본 포스팅에선 나름대로 고찰했던 내용들에 대해 써내려보고자 합니다.

본론


작업의 방향성에 대해 생각해보기


사실 정답이 정해져 있는 논제이긴 합니다. 당연히 둘 다 해야죠.
문제는 우리에게 주어진 시간은 무한한 자원이 아니라는 것입니다. 그렇기 때문에 작업을 하기 이전에, 방향성에 대해 심사숙고해보면 좋을 것 같습니다.

먼저, 제가 프로젝트의 방향성에 대해 접근하게 된 사고를 설명드려야겠네요.
제가 커널360에 합류했던 이유는 서비스 기업에 이직을 하기 위해서, 부차적인 목표는 서비스 기업에서 활용하는 기술로 포트폴리오를 만들기 위해서였습니다. 그러니까..
서비스 기업에 가고싶음 -> 그러려면 기술에 대한 이해도와 경험이 필요함 -> 그래서 프로젝트를 진행하면서 몰입할 수 있는 부캠을 고름
이렇게 되겠네요.

이직을 하고싶었던 이유도 명확했습니다. 레거시 코드가 상당했고, 그로 인한 단순/반복적인 업무.. 그리고 여기서 오는 피로감과 커리어에 악영향을 끼칠 수 있겠다라는 판단이 생겨 이직 준비를 하게 되었습니다.
이러한 배경을 토대로, 부트캠프에서 진행하는 프로젝트는 반드시 기술적인 도전과 클린 코드를 위한 노력을 많이 하겠다! 라는 목표가 있었습니다. 그렇기 때문에 현재 진행 중인 프로젝트도 기능은 최소화하고, 코드 품질을 향상시키는 것을 지표로 삼았습니다.

오루리 팀은 리팩토링을 선택했습니다.


그러나 인간은 욕심이 굉장히 많은 생물입니다. 그게 생산성이 됐든, 리팩토링이 됐든 말이죠.
따라서 커널360의 오루리 팀은 기능에 대한 '욕심'을 버리는 것부터 시작했습니다.
기능 구현이야 어찌저찌 굴러'만' 가는 코드를 만든다면, 만들수는 있겠죠. 하지만 요즘같이 개발자가 넘쳐나는 시대에 단순히 구현 가능한 개발자는 메리트가 없습니다. '협업하기 좋은 코드'를 만들기 위해 코드의 품질을 높이기로 했고, 아키텍처를 계속해서 뜯어고쳤습니다.
덕분에 구조를 바꾸고, 동기로 돌아가던 기능을 비동기로도 돌려보고, 여러가지 시도를 거쳤습니다.

당장 제가 담당했던 로그인만 하더라도, 아래와 같이 여러번 뜯어고쳤네요.

처음엔 프론트에서 로그인을 관리하다가, 백으로 role을 이전했고, OAuth2Client를 쓰다가 기능이슈가 있어서 갈아엎고... RestTemplate으로 api에서 OAuth 서버 api 호출하던걸 OpenFeign으로 바꾸고..

사실 되기'만' 하는게 중요했다면 애초에 NextAuth에서 끝났을 겁니다. 시간도 한달을 절약할 수 있었구요.
그렇지만 우리가 실제로 사용하는 서비스의 로그인은 대부분 백의 영역에서 발생하는데, 그렇게 하는 이유는 유저의 정보를 핸들링하는 것은 백이 더 유리하기 때문입니다. 따라서 백과 프론트의 적절한 role 분배를 위해 이러한 절차를 거쳤습니다.

다른 작업은 테스트코드의 Fixture를 어떻게 간편하게 사용할 수 있을까? 라는 고민을 가지고 시작하게 되었습니다.

RevewDto안에 UserDto가 있고, 이를 create해주는 함수들이 private으로 선언되어있었다.

테스트코드를 작성할 때 Fixture를 만들어야 합니다. 여기서 필요한 해당 엔티티나 DTO를 만들어줄 때 create 함수를 사용하고 있었습니다. 기존 오루리 코드는 여러개의 테스트코드에 동일한 create함수가 들어가서, 코드의 중복이 발생하고 코드를 작성하면서 귀찮은 일이 많이 발생했습니다.

User는 여러 곳에서 사용된다.

따라서 이를 Object Mother라는 패턴을 적용해서 public으로 관리하는 클래스를 별도로 만들어냈습니다.

그리고 user의 요소(id, 닉네임, 생년월일 등)를 디폴트값을 설정해두고, 빌더 패턴을 활용해 사용할 때 즉석으로 바꾸도록 매우 간편한 코드가 완성되었습니다. 아래와 같이 Fixture 코드를 작성해두고..

@Getter
@Builder
public static class TestReviewDto {
    private @Builder.Default Long id = 1032452L;
    private @Builder.Default String content = "reviewContent";
    private @Builder.Default List<String> images = List.of("image", "image2", "image33");
    private @Builder.Default float score = 2.5f;
    private @Builder.Default int interestCount = 1;
@DisplayName("암장ID, 유저ID, 커서를 받아 암장의 리뷰를 조회한다.")
@Test
void when_GetGymIdAndUserIdAndCursor_Then_RetrieveReviews() {
    // given
    Long gymId = 1L;
    Long userId = 1L;
    Long cursor = 1L;
    GymDto gymDto = createGymDto().build().get();
    List<ReviewDto> reviewDtos = List.of(
            createReviewDto(1L).build().get(),
            createReviewDto(2L).build().get(),
            createReviewDto(3L).build().get()
    );

이처럼 테스트코드를 작성할 때 간편하게 사용할 수 있었습니다.

그래서 생산성은 포기함?


리팩토링을 하는 것은 곧 생산성을 위함입니다. 저희 팀이 이렇게 리팩토링에 집중한 이유는 서비스 지속을 위해서인데요.

오루리 팀의 기능 자체는 큰 볼륨으로 커뮤니티/지도 및 리뷰/크루 시스템으로 나뉩니다.
약 4개월이라는 전체 프로젝트 기간을 %로 환산해봤을 때, 이러한 리팩토링으로 90%의 시간을 사용했다면, 신규 시스템인 크루 서비스에선 10%밖에 시간이 소모되지 않았습니다. 그만큼 강력한 확장성을 가진 코드를 작성했고, 추후 기능 추가도 무궁무진한 가능성이 있습니다.

따라서 시작은 코드의 품질을 높이고 Readable한 코드를 만들자! 는 목적으로 리팩토링 위주의 프로젝트를 진행했으나, 확장에 유리한 코드가 나오는 의외의 긍정적인 결과도 목도할 수 있었습니다.

개인적으로는 기간에 따라 생산성과 리팩토링의 중요도가 갈릴 것 같습니다.

마치며


이렇게 생산성과 리팩토링에 관해서 개인적으로 고민을 좀 해봤습니다.
결론은, 둘 다 틀린건 아니지만 장기적으로 봤을 땐 리팩토링이 좀 더 중요하지 않나... 라고 생각합니다.

소프트웨어는 만드는 것보단 유지보수가 더 중요하니까요 :)

728x90
반응형