JPA

모바일 앱 백엔드를 개발하면서 눈이 많이 뜨였다. 설계자의 시점에서 프로젝트를 바라볼 필요가 있기에 많은 노력과 고생이 들어갔다. 아직 대학교 4학년이지만 어느정도 프로젝트를 거시적으로 바라보며 이 작업이 왜 필요하고 어떻게 설계하면 좋을지에 대한 감각이 생긴것 같아서 행복한 요즘이다. 이번 시간엔 카카오 OAuth 2.0 + Spring Security + JWT + Redis를 조합하여 인증 시스템을 설계한 과정을 포스팅해볼 것이다. 각각의 기술을 선택한 이유와 세부적으로 필터체인을 설계한 과정을 공유할 생각이다. 왜 JWT일까? 전통적인 웹 애플리케이션에선 세션 방식이 일반적이다. 로그인할 땐 브라우저가 자동으로 쿠키 전송 → 서버가 세션 저장소에서 조회 → 사용자 정보 확인 이후의 ..
백엔드 개발자로서 Spring 프레임워크를 사용하다 보면 가장 많이 마주하는 어노테이션 중 하나가 바로 @Transactional이다. 하지만 어노테이션만 붙였다고 해서 모든 것이 해결되진 않는다. 분명히 붙였는데 롤백이 안되거나 트랜잭션이 아예 시작조차 되지 않는 상황인 self-invocation 문제를 CS적인 관점에서 파헤쳐보자. 그리고 마지막으로 전체적인 스프링 트랜잭션의 로드맵을 정리해볼 것이다. 왜 롤백이 안될까?다음과 같은 티켓 예매 로직이 있다고 가정해보자. @Servicepublic class TicketService { // 외부에서 호출되는 진입점 (트랜잭션 없음) public void processReservation() { log.info("예매 프로세스..
최근에 대규모 트래픽을 처리하는 선착순 티켓팅 시스템을 구축해보고 있다. 시스템 구조는 명확했다. 요청 폭증 → Kafka로 이벤트 전달Kafka 단일 파티션 + 단일 ConsumerConsumer가 이벤트를 하나씩 처리하며 DB 반영"이 정도면 동시성 문제는 해결된 거 아닌가?" 처음엔 그렇게 생각했다. 그런데 실제 구현 과정에서 가장 위험한 지점은 kafka가 아니라 DB 반영 순간이었다. 이번 글에선 JPA의 Dirty Checking이 왜 선착순 시스템에서 위험한지, 그리고 왜 원자적 연산을 선택했는지 알아보겠다. Dirty Checking만 믿어도 될까? 처음 작성한 코드를 보자. @Transactionalpublic void consume(ParticipationEvent event) { ..
문제 상황 개발하면서 크루 멤버 정보를 조회하는 API를 만들다가 LazyInitializationException 에러를 마주쳤다. GET /v1/crews/{crewId}/members/{userId} 특정 크루의 특정 멤버 정보를 조회하는 API였는데 500 에러가 발생했다. org.hibernate.LazyInitializationException: could not initialize proxy [User#123] - no Session 서비스에서 조회는 잘 됐는데 Response DTO로 변환하는 과정에서 member.getUser().getNickname()에 접근하는 순간 예외가 터졌다. 문제는 JPA의 지연 로딩 때문이었다. 내가 만든 CrewMemberEntity엔 이런 연관관계가..
공부를 하다 보면 왜 요즘 프로젝트엔 옛날과 다르게 DAO가 없고 Repository만 있을지 궁금증을 한 번쯤 가져봤을 거라고 생각한다. 오늘은 자바 진영에서 데이터 접근 계층이 어떻게 진화해왔고 왜 Repository가 표준이 되었는지 파헤쳐보는 시간을 가져보겠다. DAO 이전의 암흑기 2000년대 초반, Java 웹 개발은 JSP와 JDBC의 세상이었다. 그 시절 코드를 보자. " + rs.getString("name") + ""); } rs.close(); stmt.close(); conn.close();%> 저 시절엔 여러 문제점이 있었다. 비즈니스 로직과 DB 로직이 뒤섞임 -> 유지보수 지옥 SQL Injection 취약 -> 보안 리스크 Connection ..
JPA 리포지토리에선 메서드 이름만으로 데이터베이스 쿼리를 자동 생성할 수 있다. findByName, countByAge, deleteByStatus 같은 메서드명을 작성하면 Spring Data JPA가 이걸 분석해서 적절한 SQL을 만들어서 실행해준다. 복잡한 SQL 작성 없이도 직관적인 메서드명으로 대부분의 우리가 상상할 수 있는 범위의 데이터 조작은 다 가능하고 덕분에 개발 생산성을 크게 향상시킬 수 있다. 실무에서 자주 사용하는 패턴들을 중심으로 체계적으로 알아보자. 기본 조회 단순 조회findBy필드명 - 해당 필드로 찾기findByName("김철수") -> WHERE name = '김철수'가장 기본적인 패턴으로 특정 값과 정확히 일치하는 데이터를 찾는다.findBy필드명And다른필..
[김영한 강사님 JPA 활용 2편을 듣고 공부한 내용 정리입니다] JPA로 개발하다 보면 성능 문제를 한 번씩 겪게 된다. 대표적인 예시 상황으로 쇼핑몰 주문 조회 API를 구현해볼 것이다. 관리자가 보는 주문 목록 화면을 만들어야 한다.주문번호고객명주문일배송지주문상품#001홍성민2025-05-27서울 강북구페퍼로니 피자, 콜라#002정인선2025-05-27서울 광진구마르게리타 피자 데이터 구조 (엔티티 관계)@Entitypublic class Order { // 주문 @Id @GeneratedValue private Long id; @ManyToOne(fetch = FetchType.LAZY) private Member member; // 주문한 ..
SQL 문법을 JPA로 변환하면 어떤 문법이 될지 공부하는 김에 블로그에도 간단하게 시도해본다. 간단하게 자주 사용하는 SQL 구문을 JPA로 변환하는 것에 집중해보았다. 1. 단일 행 조회 (SELECT) SQLSELECT * FROM users WHERE id = 1; Spring Data JPA@Repositorypublic interface UserRepository extends JpaRepository { Optional findById(Long id);} 먼저 처음으로 설명을 해보겠다. 이후에는 비슷하므로 설명은 생략함 @Repository 어노테이션은 스프링에서 데이터베이스 작업을 위한 저장소 인터페이스임을 나타내기 위해 사용한 어노테이션이다. UserRepository 인터페이스는..
hskhsmm
'JPA' 태그의 글 목록