트러블슈팅
11 posts
[JPA] @Transactional이 없다고 레포지토리가 왜 터져?

🧶 문제상황 서버 오픈 전 부하테스트로 대비하기의 경험으로, 요즘 코드를 짤 때 외부 호출이 끼어있다면 트랜잭션을 최소로 잡고 있다. 거기다 더티 체킹도 사용하지 않고 명시적으로 다시 저장해주는 방식을 사용 중이다. 이건 교휸이 있어서는 아니고, 그냥 성향이다. 더 읽기 명확한 것 같고? 여기에 아이템을 삭제하는 기능을 추가 개발하게 되었다. 그리고 테스트를 실행하니, 아래와 같은 에러 메시지를 던지며 터졌다. No EntityManager with actual transaction available for current thread - cannot reliably process ‘remove’ call; nested exception is javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably…

May 07, 2023
JPA
트러블슈팅
[JPA] 실종된 데이터와 연관 객체 조회하기

백엔드 면접 단골 질문 중 하나가 이다. 물론 몇 달 전까지 나도 줄줄 외우고 있었다. dto로 갖고 오던가, 페치 조인이 엔티티 그래프가 어쩌고 배치 사이즈를… 그리고 몇 개는 실제로 적용해서 문제를 해결해보기도 했지만, 비교적 전형적인 케이스만 다뤘기에 적용했더니 해결됐다, 끝! 이었다. 그래서 최근에 을 해결하기 위해 페치 조인을 쓰다가 동작을 잘 모른다는 걸 알게 되었다. 🎁 문제 상황 단방향 연관관계를 가진 두 엔티티가 있다. 는 유저가 획득한 아이템이다. 는 아이템을 획득, 또는 사용될 때 생기는 로그다. 엔티티 , 는 다음과 같은 비즈니스 로직을 가진다. 아이템 로그는 아이템에 취한 행동에 따라 , 이 정해진다. 아이템 획득은 -> 순서로 이뤄진다. 이 후 까지 로직에서 문제가 생기더라도, 은 남아야 한다. 따라서 는 아이템 로그를 가지지 않을 수도 있다. 두 엔티티는 단방향 간접참조로 연결되어있다. 아이템 로그는 아이템 획득 엔티티를 모른다. DB에는 두 테이블…

March 26, 2023
트러블슈팅
JPA
서버 오픈 전 부하테스트로 대비하기

💡 해당 글은 사내에 작성한 문서를 허가 하에 개인 포스팅용으로 적절히 수정한 글입니다. 디어에 입사하고 처음 맡은 일은 개발 중이던 신규 인터널 API 서버 개발을 마무리하고 프로덕션 환경에 올리는 것이었다. 전체 사이클에서 내 생각에 한 80% 지점에 참여하게 되었다. 현재 신규 서버는 프로덕션 환경에 올라간 채로 ‘회사 사람들이 디어 앱을 통해 하는 요청’ 이라는 티끌의 트래픽만 받고 있는데, 다음 2주 간의 스프린트에 전체 트래픽을 100% 전환하는 게 목표다. 그래서 이번 스프린트 중에 이 서버의 성능을 테스트하고, 운영 환경에 적절한 인프라 설정값을 찾는 과제를 수행했다. 🏗 인프라 구조 모든 인프라 구조도는 극히 단순화하여 그렸다. 기존 프로덕션 인프라 구조 신규 인터널 서버는 기존 디어 서버에서 외부 API 연동이 잦은 특정 도메인을 분리한 서버다. 기존에는 디어 앱에서 특정 요청이 들어오면, 디어 서버를 거쳐, 경우에 따라 또 다른 인터널 서버를 경유해 로직을 수행…

March 12, 2023
트러블슈팅
Querydsl만의 아늑한 🏡 지어주기

🧶 문제상황 🪐 비대해진 서비스 코드 줍줍은 요청에 따라 동적으로 변화해야 하는 복잡한 조회 조건을 걸기 위해 을 도입했다. 예전에 공부하며 가볍게 사용해 본 적은 있지만, 줍줍 요구사항만큼 복잡한 코드를 짜 본 적은 없었다. 에 코드를 넣는 식으로 우선 구현했고, 무사히 배포되었다. 그러다 을 쓰지 않는 다른 서비스 클래스 코드를 보니 ‘어… 여기는 너무 깔끔한데?‘하는 위화감이 들었다. 그리고 다시 을 쓰는 코드를 보니, 가 할 일이 에서 이뤄지고 있다는 생각이 들었다. 위에 있는 기존 의 짧은 예시 코드 내에서도 사용으로 인한 서비스 역할 비대화가 보인다. 의 경우 DB에서 데이터를 가져오는 역할을 완벽히 에 맡기고 있다. 반면, 는 DB에서 어떤 데이터를 가져올지 서비스 코드 상에 완전히 노출되어 있음을 볼 수 있다. 그래서 각 레이어 별 역할이 원칙대로 분리되도록 코드를 별도의 객체로 빼내는 리팩토링을 시작했다. ✂️ 일단 레포지토리 코드 분리 사용 예시를 보면 을…

October 26, 2022
Spring
트러블슈팅
etc
[JPA] EnableJpaAuditing의 내부와 CI의 소중함

💣 문제상황 줍줍에는 북마크 설정한 메시지를 모아서 보는 기능이 있다. 기존에는 이 북마크 목록을 보여줄 때, 북마크가 등록 된 메시지의 생성 시간 순서대로 보여줬다. 그러다 사용자 편의를 위해 이를 북마크 등록 시간 순서대로 보여주기로 바꿨다. 이 때, 북마크 등록 시간을 편하게 관리하기 위해 을 도입했다. 그리고 해당 PR은 순조롭게 머지 되었는데… 팀원 봄이 진행한 컨트롤러 테스트를 WebMvcTest로 개선하는 PR에서 문제가 생겼다. 봄의 로컬에서 테스트가 잘 돌아가는 상태였는데, PR의 github action에서는 테스트가 실패했다. 로 변경한 모든 용 컨트롤러 테스트가 같은 이유로 실패하고 있었다. 라는 문구가 눈에 띈다. 해당 문구로 라이브러리를 검색해보면 라는 클래스가 나온다. 사실 이 문구로 검색하면 많은 사람들이 해결법을 이미 올려두었다. 그런데 여기까지 오니, 이 대체 뭔데? 도 아니고 라고? 이거랑 은 무슨 상관이지? 등등 궁금한 점이 생겨서 코드를 조금…

October 14, 2022
Spring
JPA
트러블슈팅
줍줍 MySQL 인덱스 탐험

🧳 줍줍 MySQL 인덱스 탐험 ❗️ 해당 포스팅에서 지칭하는 책은 모두 Real MySQL 8.0 1권을 뜻합니다. 우테코 팀 프로젝트 5차 스프린트에서 성능 테스트로 톰캣 설정 최적화 / 모든 쿼리 수집 후 DB 인덱스 설정 이라는 두 개의 백엔드 과제가 있었다. 나는 이 중 DB 인덱스 설정을 맡게 되었다. 줍줍의 전체 테이블 구조는 정석 ERD보다 간략하게 표현하자면 이렇다. 쿼리 수집은 쉬웠다. 줍줍의 모든 클래스들은 를 상속했다. 는 선언하지 않은 메서드도 사용 가능한 반면, 를 상속하면 선언한 메서드만 사용 가능하다. 뒤집어 말하면 사용을 제외하면, 코드의 모든 메서드가 곧 프로젝트에 사용 중인 모든 쿼리였다. 쿼리 최적화 by.봄🌱 인덱스 최적화를 시작하기 전에 고맙게도 같은 팀 크루인 봄이 불필요햔 left outer join을 제거하는 쿼리 최적화를 해주었다! 에서 의 PK를 FK로 가진 가 해당 FK를 조건으로 조회할 때, 로 하면 불필요한 이 발생하고 …

October 04, 2022
etc
우아한테크코스
트러블슈팅
메시지 조회 테스트 개선기

🛠 메시지 조회 테스트 개선기 초난감 메시지 조회 줍줍은 슬랙 메시지를 날짜이동, 윗방향 스크롤, 아래방향 스크롤, 단어검색, 특정 채널만 보기 등 여러 조건으로 조회할 수 있다. 사용자 편의를 위해 이렇게 다양한 조회 조건을 지원하다 보니 동적 쿼리 생성이 필요해 도 도입했다. 작성된 조회 코드와 테스트 작성을 위해 파악된 케이스만 봐도 복잡도가 짐작 갈 것이다. 어떤 점을 개선하고 싶었나 해당 메시지 조회 기능 개발은 다른 팀원들이 맡았다. 그래서 올라온 PR을 봤는데, 동작은 잘 했으나 코드 개발에 참여하지 않은 입장에서 테스트가 알아보기 어려웠다. 처음 테스트를 읽었을 때 왜 읽기 어렵다고 느껴졌을까? 먼저 데이터가 문으로 들어가는 게 원인 중 하나였다. 메시지 조회를 하려면 미리 저장된 1.멤버(사용자) 2.채널 3.채널 구독 데이터가 필수로 필요했다. 기본적으로 메시지에 의존관계가 있고, 조회 시 구독하는 채널 메시지만 필터링 하는 분기가 기본으로 있었기 때문이다.…

September 28, 2022
Spring
트러블슈팅
[Spring] 모든 요구사항을 한 엔드포인트로 처리해보자! 😂

해당 포스팅은 줍줍 개발 과정 중 맞딱뜨린 문제점과 답을 찾아가는 과정을 다른 방식으로 재구성한 글이다. 생각보다 글이 길어진 고로, 문제상황과 해결방안만 보고싶다면 문제상황 빈 컬렉션 주입을 이용한 해결 예시코드 이 둘을 보기를 권한다. 간단한 게시판 서비스를 상상하기 회원 가입과 탈퇴 당신은 지나가던 고양이의 의뢰로 서버를 개발하게 되었다. 고양이는 🐱 닉네임을 입력하면 가입이 가능하고, 탈퇴할 수 있는 서비스를 만들어 주세요 💬 라고 요구했다. 그래서 나는 (그리고 이 글을 볼 대부분의 사람들은) 이건 쉽지~ 하고 간단한 컨트롤러를 만들었다. 그런데 이제 엔드포인트를 하나로 하고 이 코드를 본 고양이가 말했다. 🐱 엔드포인트를 하나로 통일할래요. 그리고 이게 회원 가입인지 탈퇴인지는 body안에 쓸게요. 이렇게요. 💬 코드가 이상해지기 시작한다. 그래도 아직 납득 가능한 수준이다. 여기에 포스팅 기능도 몽땅 넣어줘 이 코드를 본 고양이가 또 요구했다. 🐱 각자 게시글을 쓰고,…

August 27, 2022
Spring
트러블슈팅
[Spring] @PostConstruct를 막아라

문제상황 서비스 배경지식 첫번째 참고 현재 개발중인 줍줍은 슬랙 무료 워크스페이스의 사라지는 메세지들을 대신 저장하고 보여주는 서비스다. 메세지 저장을 위해 최초 어플리케이션 구동 시, 해당 워크스페이스에 속한 모든 회원 정보를 가져온다. 이 역할을 라는 클래스의 안에 작성했다. 이유는 아래와 같았다. 어플리케이션이 구동하는 동안 발생하는 신규 가입, 탈퇴 이벤트는 실시간 반영이 된다 하지만 어플리케이션 최초 구동 전, 또는 업데이트 배포로 어플리케이션이 중지된 사이에 발생한 이벤트는 반영이 안된다 따라서 재구동 시 한 번 슬랙 API를 호출하여, 유저 정보를 업데이트한다 유저 정보를 메시지에서 참조하고 있어서 저장이 선행되어야 했다. 또, 로그인 시 이 정보와 슬랙 로그인으로 받은 정보를 대조해서 자체 토큰을 발급하는 까닭도 있다. 두번째 참고 슬랙은 라는 이름으로 슬랙 API에 HTTP 요청을 보내고 응답을 받는 객체를 제공한다. 그래서 슬랙 API 호출이 필요한 곳에서…

August 23, 2022
Spring
트러블슈팅
[JPA] 상수 픽스쳐 사용 주의

문제상황 우테코 QNA 미션에서 상수로 선언된 엔티티 픽스쳐가 있었다. 레벨3 같은 팀 크루인 봄이 해당 미션을 진행하다 테스트가 터져 어쩌다보니 같이 원인을 찾게 되었다. 상황을 비슷하게 복구해보자면 이런 구성이었다. 원본 미션 코드에서는 픽스쳐가 로 별도 클래스에 선언되어 있었다. 그런데 테스트 클래스 하나에서만 사용되고 있어서 해당 클래스로 옮겼던 것 같다. 나도 픽스쳐를 옮겼다가, 평소 쓰는 방식대로 한다고 지웠었다. 어쨌든 와 는 의 단방향 연관관계였고, 는 를 통해 저장해주는 상태였다. 그러다 테스트가 터지며 의문의 에러메시지를 마주했다. org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [FK83S99F4KX8OIQM3RO0SASMPWW]; nested exception is org.hibernate.exception.Con…

August 13, 2022
JPA
트러블슈팅
[git] 슬랙한테 봇 강퇴당하고 서브모듈 도입한 이야기

슬랙이 봇을 강퇴시켰어요 서버에서 슬랙 API를 호출하려면 발급받은 봇 토큰이 필요하다. 해당 정보를 기밀이라고 생각하지 않았는데… 애초에 봇에게 호출할 수 있는 API 제한이 있다 연결된 워크스페이스가 개발을 위해 생성된 줍줍 팀 워크스페이스다 라는 이유에서였다. 그래서 깃헙에 을 포함한 을 올려놨는데… 어느날 API 응답이 오지 않았다. 개발자 도구의 응답에는 이라는 에러 문구가 떴고, 분명 워크스페이스에 정상 추가했던 봇이 사라져 있었다. 슬랙이 봇을 강제 추방시킨 것이었다… 얼마간 잘 썼기에, 일정 주기로 공개 저장소들을 크롤링 해 한번에 정지시키는 게 아닐까 싶다. 설명서를 잘 읽자 슬랙 API에 들어가면 가 떡하니 명시되어 있다. 봇 토큰에는 큰 권한이 있습니다. 토큰은 사용자가 앱을 설치함으로써 권한을 부여했음을 나타냅니다. 앱을 설치한 유저가 준 신뢰를 해치지 않도록 토큰을 안전하게, 기밀로 보관하세요. 최소한 공개적인 형상관리에 토큰을 올리지 마세요. 환경 변…

July 25, 2022
etc
트러블슈팅