✍️
dodeon
  • 개발왕, 도던
  • 스프링 시큐리티 인 액션
    • 오늘날의 보안
    • 안녕! 스프링 시큐리티
    • 사용자 관리
    • 암호 처리
    • 인증 구현
    • 실전: 작고 안전한 웹 애플리케이션
    • 권한 부여 구성: 액세스 제한
    • 권한 부여 구성: 제한 적용
    • 필터 구현
    • CSRF 보호와 CORS 적용
    • 실전: 책임의 분리
    • OAuth 2가 동작하는 방법
    • OAuth 2: 권한 부여 서버 구현
  • 스프링 고급편
    • 스레드 로컬
    • 템플릿 메서드 패턴과 콜백 패턴
    • 프록시 패턴과 데코레이터 패턴
  • 스프링 입문
    • 프로젝트 환경설정
    • 스프링 웹 개발 기초
    • 회원 관리 예제 - 백엔드
    • 스프링 빈과 의존 관계
    • 회원 관리 예제 - MVC
    • 스프링 DB 접근 기술
      • JDBC
      • JPA
    • AOP
  • 스프링 핵심 원리
    • 객체 지향 설계와 스프링
      • 스프링의 탄생
      • 객체 지향 프로그래밍
      • 좋은 객체 지향 설계의 원칙
      • 객체 지향 설계와 스프링
    • 스프링 핵심 원리 이해
      • 회원 도메인 개발
      • 주문 도메인 개발
    • 객체 지향 원리 적용
      • 관심사의 분리
      • 새로운 구조와 정책 적용
      • 정리
      • IoC, DI, 컨테이너
      • 스프링으로 전환하기
    • 스프링 컨테이너와 스프링 빈
      • 스프링 빈 기본 조회
      • 동일 타입이 둘 이상일 때 조회
      • 상속일 때 조회
      • BeanFactory와 ApplicationContext
      • 다양한 설정 형식
      • 스프링 빈 설정 메타 데이터
    • 싱글턴 컨테이너
      • @Configuration과 싱글턴
    • 컴포넌트 스캔
      • 탐색 위치와 기본 탐색 대상
      • 필터와 중복 등록
    • 의존 관계 자동 주입
      • 롬복과 최신 트렌드
      • 조회 빈이 2개 이상일 때
      • 애너테이션 직접 만들기
      • 조회한 빈이 모두 필요할 때
      • 올바른 실무 운영 기준
    • 빈 생명 주기 콜백
      • 인터페이스 방식
      • 메서드 지정 방식
      • 애너테이션 방식
    • 빈 스코프
      • 프로토타입 스코프
      • Provider
      • 웹 스코프
  • 스프링 MVC
    • 웹 애플리케이션 이해
      • 서버
      • 서블릿
      • 멀티 스레드
      • HTML, HTTP API, CSR, SSR
      • 자바 백엔드 웹 기술 역사
    • 서블릿
      • HttpServletRequest
      • HTTP 요청 데이터
      • HttpServletResponse
      • HTTP 응답 데이터
    • 서블릿, JSP, MVC 패턴
      • 서블릿으로 만들기
      • JSP로 만들기
      • MVC 패턴
    • MVC 프레임워크 만들기
      • 프론트 컨트롤러 패턴
      • View 분리
      • Model 추가
      • 단순하고 실용적인 컨트롤러
      • 유연한 컨트롤러
      • 정리
    • 스프링 MVC의 구조 이해
      • 스프링 MVC 전체 구조
      • 핸들러 매핑과 핸들러 어댑터
      • 뷰 리졸버
      • 스프링 MVC 시작하기
      • 스프링 MVC 컨트롤러 통합
      • 스프링 MVC 실용적인 방식
    • 스프링 MVC 기본 기능
      • 프로젝트 생성
      • 로깅
      • 요청 매핑
      • HTTP 요청의 기본 및 헤더 조회
      • HTTP 요청 파라미터
      • HTTP 요청 메시지
      • HTTP 응답
      • HTTP 메시지 컨버터
      • 요청 매핑 핸들러 어댑터
    • 스프링 MVC 웹 페이지 만들기
    • 메시지, 국제화
      • 스프링 메시지 소스
    • Validation
      • BindingResult
      • FieldError, ObjectError
      • 오류 코드와 메시지 처리
      • Validator 분리
    • Bean Validation
      • Form 전송 객체 분리
      • HTTP 메시지 컨버터
    • 로그인
      • 쿠키
      • 세션
      • 서블릿 HTTP 세션
      • 서블릿 필터
      • 스프링 인터셉터
      • ArgumentResolver 활용
    • 예외 처리와 오류 페이지
      • 오류 화면 제공
      • 필터
      • 인터셉터
      • 스프링 부트 오류 페이지
    • API 예외 처리
      • 스프링 부트 기본 오류 처리
      • HandlerExceptionResolver
      • ExceptionResolver
      • ControllerAdvice
    • 스프링 타입 컨버터
      • Converter
      • ConversionService
      • 뷰 템플릿에 적용하기
      • Formatter
    • 파일 업로드
      • 서블릿과 파일 업로드
      • 스프링과 파일 업로드
      • 파일 업로드 및 다운로드 예제
  • 자바 ORM 표준 JPA 프로그래밍
    • JPA 소개
    • JPA 시작하기
    • 영속성 관리
      • 영속성 컨텍스트
      • 플러시
      • 준영속 상태
    • Entity 매핑
      • 객체와 테이블 매핑
      • 데이터베이스 스키마 자동 생성
      • 필드와 칼럼 매핑
      • 기본 키 매핑
      • 실전 예제
    • 연관 관계 매핑
      • 단방향 연관 관계
      • 양방향 연관 관계
      • 실전 예제
    • 다양한 연관 관계 매핑
      • 다대일
      • 일대다
      • 일대일
      • 다대다
      • 실전 예제
    • 고급 매핑
      • 상속 관계 매핑
      • 매핑 정보 상속
      • 실전 예제
    • 프록시와 연관관계 관리
      • 프록시
      • 즉시 로딩과 지연 로딩
      • 영속성 전이와 고아 객체
      • 실전 예제
    • 값 타입
      • 기본값 타입
      • 임베디드 타입
      • 값 타입과 불변 객체
      • 값 타입의 비교
      • 값 타입 컬렉션
      • 실전 예제
    • 객체 지향 쿼리 언어 - 기본
      • 기본 문법과 쿼리 API
      • 프로젝션
      • 페이징
      • 조인
      • 서브 쿼리
      • JPQL 타입 표현과 기타 식
      • 조건식
      • JPQL 함수
    • 객체 지향 쿼리 언어 - 중급
      • 경로 표현식
      • fetch join
      • 다형성 쿼리
      • Entity 직접 사용
      • Named 쿼리
      • 벌크 연산
  • 스프링 부트와 JPA 활용 - 웹 애플리케이션 개발
    • 프로젝트 환경설정
    • 도메인 분석 설계
      • 도메인 분석 설계
      • Entity 클래스 개발
      • Entity 설계 시 주의점
    • 애플리케이션 아키텍처
    • 회원 도메인 개발
    • 상품 도메인 개발
    • 주문 도메인 개발
      • Entity, 리포지토리, 서비스 개발
      • 주문 기능 테스트
      • 주문 검색 기능 개발
    • 웹 계층 개발
      • 변경 감지와 병합
  • 스프링 부트와 JPA 활용 - API 개발과 성능 최적화
    • API 개발 기본
      • 회원 등록 API
      • 회원 수정 API
      • 회원 조회 API
    • 지연 로딩과 조회 성능 최적화
      • Entity 직접 노출
      • Entity를 DTO로 변환
      • JPA에서 DTO 직접 조회
    • 컬렉션 조회 최적화
      • Entity 직접 노출
      • Entity를 DTO로 변환: 페치 조인
      • Entity를 DTO로 변환: 페이징과 한계 돌파
      • DTO 직접 조회
      • DTO 직접 조회: 컬렉션 조회 최적화
      • DTO 직접 조회: 플랫 데이터 최적화
      • 정리
    • OSIV와 성능 최적화
  • 스프링 데이터 JPA
    • 예제 도메인 모델
    • 공통 인터페이스 기능
      • 순수 JPA 기반 리포지토리
      • 공통 인터페이스 설정
    • 쿼리 메서드 기능
      • JPA Named Query
      • @Query
      • 파라미터 바인딩
      • 반환 타입
      • 페이징과 정렬
      • 벌크성 수정 쿼리
      • @EntityGraph
      • JPA Hint & Lock
    • 확장 기능
      • 사용자 정의 리포지토리
      • Auditing
      • Web 확장
    • 스프링 데이터 JPA 분석
    • 나머지 기능
      • Specifications
      • Query By Example
      • Projections
      • Native Query
  • Querydsl
    • 프로젝트 환경 설정
    • 예제 도메인 모델
    • 기본 문법
      • JPQL vs Querydsl
      • Q-Type 활용
      • 검색 조건
      • 결과 조회
      • 정렬
      • 페이징
      • 집합 함수
      • 조인
      • 서브 쿼리
      • Case 문
      • 상수, 문자 더하기
    • 중급 문법
      • 프로젝션과 결과 반환
      • 동적 쿼리
      • 수정, 삭제 벌크 연산
      • SQL Function 호출
    • 순수 JPA와 Querydsl
      • 순수 JPA 리포지토리와 Querydsl
      • 동적 쿼리와 성능 최적화 조회
      • 조회 API 컨트롤러 개발
    • 스프링 데이터 JPA와 Querydsl
      • 스프링 데이터 페이징 활용
      • 스프링 데이터 JPA가 제공하는 Querydsl 기능
  • 데이터 접근 핵심 원리
    • JDBC 이해
      • JDBC와 최신 데이터 접근 기술
      • 데이터베이스 연결
      • JDBC 개발
  • 백엔드 시스템 실무
    • CPU bound 애플리케이션
      • CPU를 극단적으로 사용하는 애플리케이션
      • 스트레스 테스트 툴로 성능 측정
      • Dockerized 애플리케이션 GCP 배포
      • Jenkins를 이용한 배포
    • CPU bound 애플리케이션 무중단 배포
      • nginx를 통한 로드밸런싱 구성
      • 서버를 늘려서 성능 측정
    • 배포 자동화와 협업을 위한 Git
      • GitHub Webhook과 jenkins로 배포 자동화
      • 머지할 때 발생하는 충돌 해결하기
      • 실무에서 유용한 Git 꿀팁
    • I/O bound 애플리케이션
    • Message Queue를 도입하여 데이터 유실 방지
      • 스트레스 테스트
    • 검색과 분석을 위한 저장소 ElasticSearch
    • Kubernetes
  • 모든 개발자를 위한 HTTP 웹 기본 지식
    • 인터넷 네트워크
      • IP
      • TCP, UDP
      • PORT
      • DNS
    • URI와 웹 브라우저 요청 흐름
    • HTTP 기본
      • 클라이언트-서버 구조
      • stateful, stateless
      • 비 연결성
      • HTTP 메시지
    • HTTP 메서드
    • HTTP 메서드 활용
    • HTTP 상태 코드
    • HTTP 헤더 - 일반
      • 표현
      • 콘텐츠 협상
      • 전송 방식
      • 정보
      • Authorization
      • 쿠키
    • HTTP 헤더 - 캐시
      • 검증 헤더와 조건부 요청
      • 조건부 요청 헤더
      • 프록시 캐시
      • 캐시 무효화
  • 김영한의 실전 자바
    • 제네릭
  • 예제로 배우는 스프링 입문
    • 예제로 배우는 스프링 입문(개정판)
      • PetClinic 예제
      • 스프링 IoC
      • 스프링 AOP
      • 스프링 PSA
  • 스프링 프레임워크 핵심 기술
    • 스프링 프레임워크 핵심 기술
      • IoC 컨테이너와 빈
        • 스프링 IoC 컨테이너와 빈
        • ApplicationContext와 빈 설정
        • @Autowired
        • @Component와 컴포넌트 스캔
        • 빈의 스코프
        • Environment
        • MessageSource
        • ApplicationEventPublisher
        • ResourceLoader
      • Resource/Validation
        • Resource 추상화
        • validation 추상화
      • 데이터 바인딩 추상화
      • SpEL
      • 스프링 AOP
      • Null-Safety
  • 스프링 부트 개념과 활용
    • 스프링 부트 원리
      • 자동 설정
      • 내장 서버
        • 컨테이너와 서버 포트
        • HTTPS와 HTTP2
      • 독립적으로 실행 가능한 JAR
    • 스프링 부트 활용
      • Spring Application
      • 외부 설정
      • 프로파일
      • 로깅
      • 테스트
      • Spring Boot Devtools
    • 스프링 웹 MVC
      • 소개
      • HttpMessageConverters
      • ViewResolver
      • 정적 리소스
      • 웹 JAR
      • index 페이지와 파비콘
      • ExceptionHandler
      • Spring HATEOAS
      • CORS
  • THE JAVA
    • JVM 이해하기
      • 자바, JVM, JDK, JRE
      • JVM 구조
      • 클래스 로더
      • Heap
      • Garbage Collector
    • 리플렉션
      • 클래스 정보 조회
  • The Java - Test
    • JUnit 5
      • JUnit 시작하기
      • JUnit 시작하기
    • Mockito
Powered by GitBook
On this page
  • SQL 중심적인 개발의 문제점
  • SQL에 의존적인 개발
  • 패러다임의 불일치
  • 객체와 관계형 데이터베이스의 차이
  • 상속
  • 연관 관계
  • 객체 그래프 탐색
  • 데이터 식별 방법
  • ORM
  • JPA 소개
  • 왜 사용해야 하는가?
  • 생산성
  • 유지 보수
  • 패러다임의 불일치 해결
  • 성능
  • 1차 캐시와 동일성을 보장
  • 쓰기 지연

Was this helpful?

  1. 자바 ORM 표준 JPA 프로그래밍

JPA 소개

Previous파일 업로드 및 다운로드 예제NextJPA 시작하기

Last updated 3 years ago

Was this helpful?

SQL 중심적인 개발의 문제점

SQL에 의존적인 개발

객체를 관계형 DB에 관리해야 한다. 그러다보니 SQL 중심으로 개발이 된다. 모든 쿼리를 다 짜고 자바 객체를 SQL로, SQL을 자바 객체로 변환하는 지루한 코드를 무한 반복해야한다.

패러다임의 불일치

객체는 필드와 메서드를 잘 캡슐화해서 사용하는 것이, RDBMS는 데이터를 잘 정규화 해서 저장하는 것이 목표다. 즉 서로 다른 성격이기 때문에 사용하기가 힘들다. 결국 개발자가 SQL 매퍼의 일을 하게 된다.

객체와 관계형 데이터베이스의 차이

  • 상속

    • 객체에는 있지만 데이터베이스에는 없다.

  • 연관 관계

    • 객체는 참조를 통해 데이터를 가져오지만 데이터베이스는 PK, FK로 조인을 해서 가져온다.

  • 데이터 타입

  • 테이터 식별 방법

상속

객체의 상속 관계를 비슷하게 구현할 수 있는 것이 슈퍼 타입, 서브 타입이라는 물리 모델이다.

이걸 테이블에 저장하려면 객체를 분해한 다음 insert into item, insert into album 이렇게 쿼리를 여러 번 쳐야 한다. 조회를 한다고 하면 join을 가지고 album이나 movie에 맞게 만들어줘야 한다. 이렇게 번잡해지는 문제가 있다.

list.add(album);
Album album=list.get(albumId);
// 부모 타입으로 조회하는, 다형성을 활용하는 방법도 있다.
Item item=list.get(albumId);

만약 DB 데이터가 아니라 자바 컬렉션이라면 위처럼 쉽게 할 수 있다. 하지만 RDMBS에 저장하고 꺼내는 순간 개발자가 일일이 손대야 한다.

연관 관계

member.getTeam();

객체는 참조를 사용한다.

join on m.team_id = t.team_id

테이블은 외래 키를 사용해서 join한다.

문제는 Team에서 Member 객체를 조회할 수 없다는 것이다. 테이블은 PK만 있으면 반대로도 조인할 수 있다.

테이블처럼 객체에 외래키인 teamId를 넣어서 Team을 참조하게 만들었다고 해보자.

코드로 구현하면 위와 같이 매핑할 수 있을 것이다.

근데 객체답게 설계한다면 사실 Member는 teamId가 아니라 team을 참조해야 하는 것 같다.

DB에 insert하려고 하니 team_id가 필요하다. member에서 getTeam()을 통해 id를 찾아준다.

문제는 조회다. 멤버와 팀을 각각 조회해서 연관 관계를 코드로 직접 설정해줘야 하는 번거로움이 있다.

컬렉션에 넣으면 팀이 다 한 곳에 들어가니까 연관된 데이터가 한 줄에 딸려온다. 객체지향으로 하면 이렇게 편하게 쓸 수 있다. 하지만 DB에 넣는 순간 이게 망가져버린다.

객체 그래프 탐색

객체는 레퍼런스만 있으면 어디든 쭉쭉 갈 수 있어야 한다.

하지만 처음 실행하는 SQL에 따라 검색할 수 있는 범위가 제한되어 버린다.

처음에 데이터를 가져올 때 member와 team을 대상으로 가져왔다면, getTeam()은 데이터가 있지만 order는 비어있기 때문에 꺼낼 수가 없다.

이것은 결국 Entity 신뢰 문제로 번진다. 개발자가 코드 상으로는 team과 order에 접근할 수 있지만 실제 데이터를 확인하기 전까지는 진짜 그런지 알 수가 없다.

레이어드 아키텍쳐는 그 다음 계층을 신뢰하고 있어야 하는데 이게 깨지는 것이다.

그렇다고 쓰지도 않는 객체를 미리 다 끌어올 수도 없는 일이다. 그래서 경우의 수에 따라 위처럼 조회하는 메서드를 여러 개 만들어야 한다.

물리적으로는 서비스와 DAO 계층이 이렇게 나뉘어 있어도 논리적으로는 연결되어 있는 문제가 있다. 즉, 진정한 의미의 계층 분할이 어렵다.

데이터 식별 방법

두 객체는 식별자가 같아도 SQL 실행 결과를 new Member()를 통해 새로 만들기 때문에 비교하면 다르게 나온다.

하지만 컬렉션에서 조회한다면 둘은 참조값이 같기 때문에 같게 나온다.

이렇다보니 객체 지향 설계를 하면 할 수록 매핑 작업만 늘어난다. 사람들은 객체를 자바 컬렉션에 저장하듯 DB에 저장하는 방법을 고민했고 이에 대한 대안이 JPA다.

ORM

객체는 객체답게, RDB는 RDB 답게 설계하면 ORM 프레임워크가 매핑해준다.

JPA는 애플리케이션과 JDBC 사이에서 동작한다. 자바 애플리케이션이 JPA에게 명령하면 JPA가 JDBC API를 이용해 SQL를 실행한다.

JPA에게 Member 객체를 넘기면 해당 Entity를 분석해서 적절한 쿼리를 생성한다. 그 쿼리를 JDBC API에게 보내고 결과를 받는다.

중요한 건, 쿼리를 개발자가 아니라 JPA가 만든다는 것이다.

조회할 때도 PK만 보내면 JPA가 적절한 쿼리를 만들어 보내고 결과를 매핑해준다.

이렇게 ORM은 패러다임의 불일치를 해결해준다.

JPA 소개

옛날에도 EJB라는 ORM이 있었지만 성능이나 기능이 좋지 않아 잘 쓰이지 않았다. 그걸 개선한 게 하이버네이트고 자바 진영에서 하이버네이트 개발자를 데려와 표준으로 만든 것이 JPA다.

JPA는 인터페이스의 모음이다. JPA 2.1 표준 명세를 구현한 구현체는 하이버네이트, EclipseLink, DataNucleus 세 가지가 있다. 대부분은 하이버네이트를 사용한다.

왜 사용해야 하는가?

  • SQL 중심적인 개발에서 객체 중심으로 개발 가능

  • 데이터 접근 추상화와 벤더 독립성

  • 표준

생산성

저장, 조회, 수정, 삭제에 대한 SQL이 필요없고 그냥 만들어져 있는 메서드를 쓰기만 하면 된다.

특히 수정은 내가 원하는 값만 넣으면 DB로 수정 쿼리를 보내준다. 리스트에 있는 값을 수정해서 다시 리스트에 넣는 행위를 하지 않듯 JPA는 이것을 자동으로 해준다.

유지 보수

예전에는 개발자가 쿼리를 하나하나 수정해야 했다.

이제 필드만 추가하면 SQL은 JPA가 처리한다.

패러다임의 불일치 해결

JPA는 상속과 같은 패러다임 불일치 문제도 해결해준다. persist()에 자식만 넣으면 부모와 자식 모두 쿼리를 날려준다.

조회할 때도 상속 관계의 Entity에 대해 알아서 join 해준다.

연관 관계를 지정했다면 Member만 조회해도 연관 관계가 있는 Team도 함께 조회할 수 있다. 마치 자바 컬렉션에 넣었던 것처럼.

이전엔 쿼리가 Member, Team만 불러왔기 때문에 Order를 불러올 수 없었다. 하지만 이제 객체 그래프를 자유롭게 탐색할 수 있다.

JPA에는 지연 로딩이 있어서 해당 데이터가 필요할 때 SQL을 쳐서 가져올 수 있다.

같은 트랜잭션 안에서 조회한 Entity는 같다는 걸 보장한다.

성능

시스템에 중간 계층이 있다면 캐시 등을 이용해 성능을 높일 수 있다. JPA도 이 중간 계층에 해당한다.

1차 캐시와 동일성을 보장

만약 로직이 너무 복잡해서 중간 중간에 계속 같은 멤버를 조회하는 일이 생긴다면, 처음 조회한 m1은 SQL로 가져오지만 같은 PK이 값으로 다시 조회하면 캐시에서 가져온다.

여기서 캐시는 일반적인 캐시가 아니라 한 트랜잭션 안에서만 보장되는 짧은 캐시다. 실무에서 성능적으로 크게 도움은 안되지만 매커니즘을 알아두자.

쓰기 지연

똑같은 쿼리가 3개가 있다면 네트워크도 3번 타야 해서 느리다. JDBC Batch 기능이 있긴 하지만 아주 복잡해서 쓰기가 힘들다.

JPA는 일단 메모리에 쌓았다가 커밋되는 순간 같은 쿼리는 한 네트워크로 보낸다. 트랜잭션이란 기능이 있기 때문에 일단 메모리에 요청을 쌓았다가 커밋하는 순간에 네트워크로 보낸다.

Member 객체를 찾을 땐 Member 객체만 가져오고, 그 안에 속한 Team 데이터는 필요할 때 쿼리를 날려서 가져오는 것이 지연 로딩이다. 그만큼 쿼리가 많이 나가는 단점이 있다.

즉시 로딩은 join을 통해 한 방에 가져오는 기능이다. Member를 가져올 때 Team을 같이 가져온다. Team을 실제 가져오는 시점에는 이미 로딩된 데이터를 사용한다.

실무에서는 일단 지연 로딩으로 해놓고 최적화가 필요한 부분에 즉시 로딩을 사용한다.