우아한형제들에 외부 개발자분들을 초대해서 JPA 기본기를 강의한 소감
배달의민족 내부 시스템 JPA 적용 사례 공유

안녕하세요. 프론트서버개발팀 김영한입니다.

이번에 외부 개발자분들을 초대해서 JPA 강의를 진행한 소감과 우아한형제들이 실제 JPA를 어떻게 사용하고 있는지 두 가지를 공유하겠습니다. 참고로 이번에 진행한 강의 파일의 다운로드 링크는 마지막에 첨부해 두었습니다.

어떻게 시작했는가?

최근에 JPA 기본기 강의를 외부에서 진행했는데, 사내 신입 개발자분들도 이 강의를 듣고 싶다는 요청이 있었습니다. 마침 강사도 사내에 있으니 일이 바로 진행되었습니다. 그리고 이번 기회에 사내 뿐만 아니라 외부 개발자 분들도 초대하기로 했습니다.

우아한 Developer Relations 두번째 이야기 - 우아한형제들 기술 블로그 참고

내부 강의에 외부 개발자를 초대?

사내에서 30분, 외부에서 10분을 초대하기로 했습니다. 사내에 열정있는 개발자분들이 많아서 사내 모집은 5분 만에 정원이 다 찼습니다. 외부 분들은 감사하게도 250명이 지원해주셨습니다. 세미나를 하는 입장에서 더 많은 분들을 초대하고 싶었지만 실습형 세미나여서 인원을 제한했습니다. 향후 강의형 세미나를 하게되면 100명이 넘는 분들도 초대할 수 있을 것 같습니다.

강사 입장에서 외부 분들이 오면 더 많이 긴장하고, 더 많이 준비하게 됩니다. 결국 세미나 자체의 질이 좋아집니다. 내부 구성원, 외부 개발자, 그리고 강사 모두에게 도움이 되는 방향이라 생각합니다. 그리고 저희 회사에 관심있는 분들에게 조금이나마 우아한형제들을 열어주고, 분위기도 보여줄 수 있다는 점도 좋았습니다. 외부 손님들도 자연스럽게 강의장에 오셔서 강의를 듣고 분위기를 보고, 그리고 편하게 떠날 수 있어서 좋았습니다.

기술적인 관점에서 내부 개발자들은 내부 시스템을 서로 잘 알고있어서 다양한 질문이 나오기가 어려운데, 다른 환경에서 관련 기술을 다르게 사용하는 분들의 이야기를 들을 수 있다는 장점도 있었습니다.

JPA 세미나 모집 공고

강의 목표는?

최근에는 스프링 부트나 스프링 데이터 JPA처럼 추상화된 기술 기반에서 JPA를 사용합니다. 그래서 실제 JPA가 어떻게 동작하는지 내부 구조나 원리를 이해하기는 쉽지 않습니다. JPA 자체를 기본부터 이해하면 JPA를 더욱 잘 활용할 수 있고, 문제가 발생했을 때 근본적인 원인을 쉽게 찾을 수 있습니다.

JPA를 실무에서 잘 사용하려면 다음 두 가지를 기본으로 이해해야 합니다. 그래서 이번 강의에서는 이 부분에 초점을 맞추었습니다.

  • 연관관계 매핑
  • 영속성 컨텍스트

연관관계 매핑

객체 세상은 객체 지향적으로 잘 설계하고, 관계형 데이터베이스 세상은 테이블을 잘 정규화해서 설계하는 것이 중요합니다. 하지만 각각의 사상에 맞도록 잘 설계해도, 객체를 테이블에 넣는 과정에서 둘의 차이점 때문에 수많은 어려움이 발생합니다. 예를들어서 객체는 상속관계가 있지만 관계형 데이터베이스의 테이블은 객체와 같은 상속관계가 없습니다. 그리고 객체는 참조(레퍼런스)를 통해서 연관관계를 맺지만, 테이블은 외래 키를 통해서 연관관계를 맺습니다. 결국 이런 차이점을 메우기 위해 개발자가 중간에서 한땀한땀 SQL을 작성하고, SQL 쿼리 결과를 객체에 맞도록 변환해야 합니다. JPA와 같은 ORM 프레임워크는 객체와 관계형 데이터베이스를 매핑해서 둘의 차이점을 중간에서 해결하고, 심지어 SQL도 대신 작성해서 실행합니다. 그래서 애플리케이션 개발자는 객체 설계와 개발에 더 집중할 수 있습니다. 다만 이런 이점을 얻으려면 객체와 테이블을 정확하게 매핑하는 것이 중요합니다. 테이블 하나를 객체에 매핑하는 것은 쉽습니다. 하지만 복잡하게 연결된 테이블과 객체를 매핑하는 것은 쉽지 않고, 특히 객체를 양쪽 방향으로 참조하게 매핑하는 것은 객체와 테이블의 차이 때문에 더 어렵습니다. 실무에서 대부분의 테이블은 다른 테이블과 관계가 있으므로, 이번 강의는 연관관계를 잘 매핑하는 부분에 초점을 맞추었습니다.

영속성 컨텍스트

연관관계 매핑이 객체와 테이블의 매핑 정보를 구성하는 정적인 부분이라면, 영속성 컨텍스트는 JPA 내부 동작 방식을 이해하기 위해 필요한 동적인 부분입니다. JPA를 사용하면 객체를 자바 컬렉션에 관리하는 것 처럼 편리하게 데이터베이스에 관리할 수 있습니다. 예를들어서 객체를 자바 컬렉션에 저장하는 시나리오를 생각해보면, 객체를 자바 컬렉션에 넣기만 하면 됩니다. JPA를 사용하면 JPA가 제공하는 저장 API에 객체를 넣기만 하면, JPA가 매핑 정보를 읽어서 적절한 INSERT SQL을 만들고, 해당 객체를 데이터베이스에 저장합니다. 이번에는 데이터 변경을 예로 들겠습니다. 자바 컬렉션을 사용할 때, 컬렉션 내부에 저장된 객체의 데이터를 변경하고 싶으면, 단순하게 컬렉션에서 객체를 조회하고 해당 객체의 데이터를 변경하면 됩니다. 조회한 객체와 컬렉션에 저장된 객체의 참조값이 같기 때문이지요. 그래서 컬렉션에 변경내역을 반영해달라는 별도의 요청을 하지 않아도 됩니다.

자바 컬렉션 객체 데이터 변경

Member member = collection.get(id);
member.setUsername(newName);

그런데 데이터베이스에 저장된 객체를 수정하는 시나리오를 생각해보면, 먼저 객체를 데이터베이스를 통해 조회하고, 객체의 데이터를 변경한 다음, UPDATE SQL을 만들어서 변경 내용을 데이터베이스에 전달해야 합니다.

데이터베이스 객체 데이터 변경

Member member = db.find(id);
member.setUsername(newName);
db.update(member);

JPA를 사용하면 객체를 조회하고 객체의 데이터를 변경만 해도 변경 내용이 데이터베이스에 자동으로 반영됩니다. 트랜잭션을 커밋하는 시점에 JPA가 객체의 변경내용을 자동으로 감지하고 업데이트 쿼리를 만들어서 데이터베이스에 반영하기 때문입니다. 개발자 입장에서는 마치 자바 컬렉션에 있는 데이터를 변경한 것 처럼 동작하는 것이지요. 이런 내부 동작방식은 영속성 컨텍스트라는 컨셉을 알면 어렵지 않게 이해할 수 있습니다. 이번 강의에서는 이 영속성 컨텍스트와 내부 동작방식을 설명하는 부분에 초점을 맞추었습니다.

JPA 객체 데이터 변경

Member member = jpa.find(id, Member.class);
member.setUsername(newName);

우아한형제들은 JPA를 어떻게 사용하는가?

우아한형제들은 스프링 프레임워크와 JPA를 사내 표준 기술 스택으로 사용합니다. 그리고 대부분의 서버 개발팀이 주로 사용하는 기술은 다음과 같습니다.

  • 자바 8
  • 스프링 부트
  • 스프링 데이터 JPA
  • JPA
  • QueryDSL

참고로 표준은 기본 가이드를 제시하는 것이지 꼭 지켜야 하는 것은 아닙니다. 항상 문제를 먼저 고민하고, 어떤 기술로 해결하는것이 적절한지 고민하는 방식으로 접근합니다. 예를들어서 사용자 접점에 있고, 비즈니스 로직이 단순한 서버는 NodeJS를 사용하기도 하고, 트래픽 대응이 중요한 프론트 API 일부 서버는 완전한 Non-blocking 서비스를 구현하기 위해 리엑티브 스타일의 라이브러리를 사용하기도 합니다.

우아한형제들은 사용자 트래픽을 주로 받는 메인 화면부터 정산 같은 복잡한 배치형 프로젝트까지, 관계형 데이터베이스를 접근할 때는 대부분 JPA를 사용합니다.

우아한형제들에서 JPA를 사용한 대표적인 사례를 공유하겠습니다.

정산 프로젝트

정산 프로젝트는 과거 모 대기업에 외주를 맡긴 프로젝트였는데, 저희회사 규모에서는 제법 큰 비용과 일정을 사용했습니다. 1년 6개월의 개발과 유지보수 기간동안 특급 개발자도 여러명 참여했습니다. 프로젝트를 분석해보니 대부분 SQL에 비즈니스 로직이 녹아있고, DB 테이블 중심으로 개발이 되어 있었습니다. 수천라인의 동적 쿼리들이 수두록 했고, 하나를 수정하면 둘 이상의 버그가 생성되었습니다. 문제는 앞으로 비즈니스를 계속 확장해야 하는데, 정산이 비즈니스 로직도 성능도 다 병목지점이 되어가고 있었습니다. CTO 조직은 이 프로젝트를 기술적 파산 단계로 정의하고 사내에서 다시 개발하기로 했습니다. 재개발 프로젝트여서 정산을 운영하시는 분들이나 고객 입장에서는 크게 바뀌지는 않았지만, 기획부터 개발 프로세스, 테이블 설계까지 모든 것을 다시 시작했습니다. 기술적인 결과는 다음과 같습니다.

사용 기술 비교

  • 기존: PHP(코드 이그나이터), DB 테이블 중심 개발
  • 신규: 자바, 스프링, 스프링 배치, JPA(ORM), 객체 중심 개발 (네이티브 SQL 거의 없음)

테스트 케이스

  • 0개(기존) -> 205개(신규)

UI 코드를 제외한 순수 비즈니스 로직과 SQL을 합한 코드 라인 수 비교

  • 123,671라인(기존) -> 37,570라인(신규) (약 3.2배 감소)

이런 극적인 변화가 단순히 JPA만으로 얻어진 것은 아닙니다. 기획, 설계, 운영 프로세스 개선 등 여러 부분을 기획과 개발이 함께 고민하고 최적화 했기에 가능했습니다. JPA는 수 많은 단순 반복 쿼리들을 자동화 해 주었고, 그 덕분에 작성해야 하는 코드 라인도 많이 줄었습니다. 하지만 이보다 더 중요한 점은 JPA를 사용한 덕분에 SQL 중심에서 객체 중심으로 개발 패러다임이 손쉽게 전환되었다는 점입니다. 개발자들은 SQL에 얽매이지 않고, 객체 지향의 장점들을 적극 활용해서 애플리케이션을 더 유연하고, 견고하게 개발할 수 있었습니다.

마무리

우아한형제들의 주문, 결제, 정산 시스템은 수 억 건의 데이터를 관리하고, 매년 조 단위의 금액을 처리합니다. 그리고 성장 속도는 매년 2배씩 증가하고 있습니다. 이 시스템들 모두 JPA 기반으로 안정적으로 운영되고 있습니다. 다음에 기회가 있으면 각 시스템 별로 내부 사례들을 공유하겠습니다. 마지막으로 2019년에는 다양한 개발 세미나를 기대하셔도 좋을 듯 합니다. 자세한 내용은 다음 글을 참고해주세요. 우아한 Developer Relations 두번째 이야기 - 우아한형제들 기술 블로그 감사합니다^^

JPA 강의 자료는 다음 링크를 선택해주세요: 강의자료