유미의 기록들

[개인과제 - Spring 플러스] 레거시 코드 리팩토링 본문

대외활동 기록/내일배움캠프

[개인과제 - Spring 플러스] 레거시 코드 리팩토링

지유미 2024. 10. 11. 11:09
728x90
반응형
레거시 코드(Legacy Code)란?
나를 포함한 모든 개발자가 기존에 개발했던 코드

 

잘 작동되는 코드여도 오래된 기술을 사용할 수 있고, 현재는 사용되지 않는 코드들도 있다. 결합도가 높거나, 테스트 코드가 없거나 가독성이 떨어지는 등의 특징들을 가지고 있다

 

💡 목표

- 레거시 코드의 리팩토링을 통해 코드의 가독성과 유지보수성을 높이고 개발 생산성을 향상시킬 수 있다
- 복잡한 구조를 단순화하고, 최신 기술과 패턴을 적용함으로써 확장성과 유연성을 확보할 수 있다
- 성능을 개선하고, 보안성을 강화할 수 있으며, 변경에 대한 리스크를 줄여 안정적인 서비스 운영에 기여할 수 있다

 

📄  리팩토링한 내용

 

🚀 트러블 슈팅

코드 추가 - 4. QueryDSL 을 사용하여 검색 기능 추가

[검색 조건]
- 검색 키워드로 일정의 제목을 검색할 수 있어요.
- 제목은 부분적으로 일치해도 검색이 가능해요.
- 일정을 생성일 최신순으로 정렬해주세요.
- 담당자의 닉네임은 부분적으로 일치해도 검색이 가능해요.
        
[검색 결과]
- 일정에 대한 모든 정보가 아닌, 제목만 넣어주세요.
- 해당 일정의 담당자 수를 넣어주세요.
- 해당 일정의 총 댓글 개수를 넣어주세요.
- 검색 결과는 페이징 처리되어 반환되도록 합니다.

⚠️ 일정 담당자 수, 댓글 개수 결과 반환 안됨

처음에 일정의 담당자 수와, 총 댓글 개수를 select절에서 조회 대상을 지정할 때, `todo.managers.size()`나 `todo.comments.size()`와 같은 컬렉션의 크기를 바로 가져오려고 했다.

queryFactory
      .select(new QTodoSearchResponse(
              todo.title,
              todo.managers.size().as("managerCount"),
              todo.comments.size().as("commentCount")
      ))

 

`size()`는 컬렉션 크기를 JPA엔티티가 로드된 상태(메모리 상에서 이미 로드된 컬렉션)에서 가져오는 것이지, 데이터베이스 쿼리로 변환되는 것이 아니다. 즉 데이터베이스 쿼리에서 `COUNT`함수로 집계되지 않으므로, 실제 `COUNT`를 사용하는 것을 권장한다

⚠️ 검색 조건 닉네임을 추가하여 요청했을 때 에러 발생

arta.servlet.ServletException: Request processing failed: org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.query.SemanticException: Ambiguous unqualified attribute reference 'user' (qualify the attribute reference by an identification variable)

 

private BooleanExpression nicknameContain(String nickname) {
    return StringUtils.hasText(nickname) ? user.nickname.contains(nickname) : null;
}

 

담당자는 User테이블의 외래 키로 연결되어 있기 때문에 `user.nickname`으로 검색하려면 반드시 User 테이블을  join 해야만 닉네임에 접근할 수 있다. 하지만 Todo 테이블과 Manager 테이블만 조인을 했기 때문에 다음과 같은 에러가 발생하였다

 

수정 방법

List<TodoSearchResponse> content=queryFactory
                .select(new QTodoSearchResponse(
                        todo.title,
                        manager.count().as("managerCount"),
                        comment.count().as("commentCount")
                ))
                .from(todo)
                .leftJoin(todo.managers,manager)
                .leftJoin(todo.comments,comment)
                .leftJoin(todo.user,user)
                .where(
                    titleContain(condition.getTitle()),
                    nicknameContain(condition.getNickname())
                )
                .groupBy(todo.id)
                .orderBy(todo.createdAt.desc())
                .offset(pageable.getOffset())
                .limit(pageable.getPageSize())
                .fetch();

 

1. managers.size()나 comments.size() 대신, 각각의 엔티티를 join한 후 group by와 count를 사용해서 컬렉션의 크기를 효율적으로 계산하고, 성능 문제를 피할 수 있었다

2. 담당자, 댓글 테이블 뿐만 아니라 유저 테이블까지 조인을 해주면서 검색 조건을 추가했을 때 발생하는 에러를 해결할 수 있었다.

select
      t1_0.title,
      count(m1_0.id),
      count(c1_0.id) 
  from
      todos t1_0 
  left join
      managers m1_0 
          on t1_0.id=m1_0.todo_id 
  left join
      comments c1_0 
          on t1_0.id=c1_0.todo_id 
  left join
      users u1_0 
          on u1_0.id=t1_0.user_id 
  where
      u1_0.nickname=? 
  group by
      t1_0.id 
  limit
      ?, ?

 

 

728x90
반응형
Comments