FK 없이 QueryDSL 쓰기

두주·2024년 2월 7일
0

TIL

목록 보기
52/58

학습동기

JPA에 대해 심화 내용을 학습하던 중, RDBMS를 사용하면서도
FK를 가지지 않는 회사들이 많다는 내용을 확인했다.

왜 FK를 쓰지 않을까?

나도 겪었던 문제지만, post_id를 fk로 가지는 comment 를 작성할 때
post가 없을 때에는 comment가 작성되지 않는다.

이게 단순히 1차에서 끝나는 것이 아니라, post_id에는 user_id가 필요하고
또 첨부파일이 있으면 file_id 도 필요하고 등등등등..

이게 길어지다 보면 테스트하는데 빠른 개발이 불가하다는 것이다.

어떻게 해결할까?

현재 내가 작성 중인 프로젝트에서 어떻게 FK를 뺄까? 를 먼저 고민했다.

    override fun searchByNickname(nickname: String, pageable : org.springframework.data.domain.Pageable): Page<PostEntity> {

        // 이거도 총 개수부터
        val totalCount = queryFactory
            .select(post.count())
            .from(post)
            .join(post.user, user)
            .where (user.nickname.containsIgnoreCase(nickname))
            .fetchOne() ?: 0L

        // 페이지에 몇번째?
        val pageCount = queryFactory
            .select(post.count())
            .from(post)
            .join(post.user, user)
            .where (user.nickname.containsIgnoreCase(nickname))
            .offset(pageable.offset)
            .limit(pageable.pageSize.toLong())
            .fetch()

        return PageImpl(pageCount, pageable, totalCount)
    }

내가 바꿔보기로 한 코드는 searchByNickname으로,
닉네임으로 글을 검색하는 기능이다.

현재는 post에 user가 연관관계로 설정되어 있기 때문에
join을 통해 user data를 가져올 수 있는 상태이다.

먼저, 연관 관계를 해제해야 하기 때문에 아래와 같이 createPost를 변경했다.

    override fun createPost(request: CreatePostRequest, userPrincipal: UserPrincipal): PostResponse {
        val user = userRepository.findByIdOrNull(userPrincipal.id) 
        	?: throw UserNotFoundException (userPrincipal.id)	
        return postRepository.save(PostEntity(
            title = request.title,
            content = request.content,
            condition = request.condition,
            user_id = user.id
            user_nickname = user.nickname // jwtconfig도 수정해야 함
        )
        ).toResponse()
    }

user_id = user.id를 통해 로그인한 유저의 토큰 인증 정보를 통해 id를 가져와서,
post 테이블에 저장할 때 user_id도 함께 저장하도록 설정했다.

원래는 user = user으로 끝났다.

1. Join 없이 별도의 쿼리를 사용함

	val user = userRepository.findByNickname(nickname)
    
    if (user != null) {
    	val totalCount = queryFactory
        	.select(post.count())
            .from(post)
            .where(post.nickname.eq(nickname))
            .fetchOne() ?: 0L

장점

1) 간단하다.

단점

1) post는 post대로, user는 user대로 가져온다.
연관 관계를 맺지 않으므로 어떤 user가 작성한 post인지 확인할 수 없다.

2) 두 번의 데이터베이스 접근이 필요하다.
user에서 user찾고, post에서 post찾고.

쿼리가 두 개씩 발생한다.

2. 서브쿼리를 사용함

	val subQuery = JPAExpressions
    	.select(user.nickname)
        .from(user)
        .where(user.nickname.eq(nickname))
        
    val totalCount = queryFactory
    	.select(post.count())
        .from(post)
        .where(post.nickname.in(subQuery))
        .fetchOne() ?: 0L

장점

1) 한 쿼리 내에서 두 테이블을 조회할 수 있다.

단점

1) 복잡하다. (구현하지 못했다)

2) 가독성이 좋지 않다.

3. 두 테이블을 결합한 새로운 테이블

	val userAndPost = QUserAndPostEntity.userAndPostEntity
    
    val totalCount = queryFactory
    	.select(userAndPost.count())
        .from(userAndPost)
        .where(userAndPost.nickname.eq(nickname))
        .fetchOne() ?: 0L

장점

1) 없는 것 같다..

단점

1) 지금 걸 바꾸는게 아니라 UserEntity, PostEntity 모든걸 바꿔야 한다.
2) 사실상 의미가 없는 것이 아닐까 ?

결론

만약 FK를 사용하지 못할 경우에는 1번이 가장 좋은 방법일 것 같다.

하지만 관계형 데이터베이스를 사용하는데 FK를 안 쓸 이유가 대체 무엇일까? 라는 생각이 들었다.

개발할 때 FK로 인해서 테스트에 제약이 걸린다면 FK를 걸지 않은 상태에서 개발하고
마지막 론칭 시점 등에 FK를 걸어주면 되는 거 아닐까?

서비스 확장에 다소 불편함이 있을 수 있다는 것은 이해하지만
개발을 조금 더 쉽게 하자고 더 큰 불편함을 감수하는 것은 아닐까..


추가

FK를 쓰지 않는다고 객체지향이 아닌 것이 아니다 !!!!
db에서만 fk를 안쓰는거지 join은 문제 없다

profile
야옹.

0개의 댓글