Starting jenkins (via systemctl): Job for jenkins.service failed because the control process exited with error code. See "systemctl status jenkins.service" and "journalctl -xe" for details.
journalctl -xe에서 에러 메시지 :
jenkins[9063]: jenkins: failed to find a valid Java installation systemd[1]: jenkins.service: Main process exited, code=exited, status=1/FAILURE
systemd[1]: jenkins.service: Failed with result 'exit-code'.
systemd[1]: Failed to start My Company Jenkins Controller.
원인 : Java 설치 경로를 찾지 못함
해결 방법
1) 젠킨스 포트 번호 맞추기 (8080가 기본 포트로 설정 되어있으나 8080을 Well-Known 포트처럼 암묵적으로 많이쓰기때문에 변경필요)
- 젠킨스 config 수정
sudo vi /etc/sysconfig/jenkins
JENKINS_PORT="9100"
- 젠킨스 서비스 config 수정
sudo vi /usr/lib/systemd/system/jenkins.service
Environment="JENKINS_PORT=9100"
•자바의 오픈 소스 애플리케이션 프레임워크중 하나로 스프링의 기본 철학은 특정 기술에 종속 되지 않고 객체를 관리할 수있는 프레임워크를 제공하는것
• 컨테이너로 자바객체를 관리하면서 의존성 주입과 제어의 역전을 통해 결합도를 낮춤
2. DI
2.1 DI란
• Dependency Injection 의존성 주입
• 객체간 의존관계를 미리 설정해두면 스프링 컨테이너가 의존 관계를 자동으로 연결해줌
• 컴포넌트 스캔을 통해 하기의 어노테이션이 붙어있으면 자동으로 스프링 빈에 등록해줌
- @Component : 컴포넌트 스캔에서 사용
- @Controlller : 스프링 MVC 컨트롤러에서 사용
- @Service : 스프링 비즈니스 로직에서 사용
- @Repository : 스프링 데이터 접근 계층에서 사용
- @Configuration : 스프링 설정 정보에서 사용
• 생성자 주입 시 @Autowired를 쓰거나, 롬복의 @RequireArgsConstructor를 사용
2.2 DI 하는 방법 및 장단점
DI 방법
장점
단점
필드 주입
•코드가 간결
•변경 불가 •프레임워크에 의존적 •테스트 코드 작성시 객체수정이 불가
setter 주입
•객체 생성 이후에도 객체 변경가능 •선택적으로 생성해야하는 객체나 변경이 발생하는 의존관계에서 사용
•public으로 구현하기 때문에, 관계를 주입 받는 객체의 변형 가능성을 열어둠
생성자 주입
•생성자의 호출 시점에 1회 호출 되는 것이 보장 •주입받은 객체가 변하지 않거나, 반드시 객체의 주입이 필요한 경우에 강제하기 위해
3. IoC (Spring 3대요소 중 하나)
• Inversion of Control 제어의 역전
• 제어권이 사용자에게 있지 않고 프레임워크에 있어서 필요에 따라 사용자의 코드를 호출함
• 스프링에서는 인스턴스의 생성부터 소멸까지 개발자가 아닌 컨테이너에서 대신 관리함
4. AOP (Spring 3대요소 중 하나)
4.1 설명
• 관점 지향 프로그래밍
• 여러 객체에 공통으로 적용 할 수 있는 기능을 분리하여 개발자는 반복작업을 줄이고 핵심 기능 개발에만 집중할 수 있음
• Object Oriented Programming(객체지향 프로그래밍)을 보완 할 수 있는 패러다임
※ 여러 오브젝트에 나타나는 공통 부가기능을 모듈화하여 재사용
4.2구현 방법
※어떤 부가기능을 어디에 적용시킬지 파악
•Advice
- Joinpoint에서 실행되어야 하는 프로그램 코드
- 독립된 클래스의 메소드로 작성 - 실질적으로 어떤 일을 해야할 지에 대한 것, 실질적인 부가기능을 담은 구현체 - 관심사를 구현한 소스코드
- 종류 5가지
@Before : 메서드가 실행되기전에 사용되는 Advice
@AfterReturning: 메서드가 정상적으로 실행되었을 때 사용되는 Advice
@AfterThrowing : 메서드가 예외를 발생 시켰을 때 사용되는 Advice
@After : 메서드가 정상적으로 실해되거나, 예외를 발생시켰을때 사용되는 Advice
@Around : 비지니스 로직 전후 실행되는 Advice
•JoinPoint
- Advice가 적용 될 수 있는 위치, 어플리케이션 실행 흐름에서의 특정 포인트(AOP를 적용할 수 있는 지점)를 의미
- 메소드를 호출하는 '시점', 예외가 발생하는 '시점'과 같이 애플리케이션을 실행할 때 특정 작업이 실행되는 '시점'을 의미 - Advice를 적용할 수 있는 후보 지점 혹은 호출 이벤트 - Advice가 적용될 위치, 끼어들 수 있는 지점. 메서드 진입 지점, 생성자 호출 시점, 필드에서 값을 꺼내올 때 등 다양한 시점에 적용가능 - 관심사를 구현한 코드를 끼워 넣을 수 있는 프로그램의 이벤트를 말하며, 예로는 call events, execution events, initialization events 등이 있음
•PointCut
- joinpoint의 상세한 스펙을 정의한것, 언제 Advice를 실행할지를 정의 - Target 클래스와 Advice가 결합(Weaving)될 때 둘 사이의 결합규칙을 정의 - 예로 Advice가 실행된 Target의 특정 메소드 등을 지정 - JoinPoint의 상세한 스펙을 정의한 것. 'A란 메서드의 진입 시점에 호출할 것'과 같이 더욱 구체적으로 Advice가 실행될 지점을 정할 수 있음 - 관심사가 주프로그램의 어디에 횡단될 것인지를 지정하는 문장이며, 에로는 before call(public void update*(...))등이 있음
5. PSA (Spring 3대요소 중 하나)
• Portable Service Abstraction. 필요에 따라 바꿔 끼울 수 있는 서비스 추상화
•Service Abstraction으로 제공되는 기술을 다른 기술 스택으로 간편하게 바꿀 수 있는 확장성을 갖고 있는 것
• 요청에 대한 작업 전/후로 가로챔(Dispatcher Servlet, controller 호출 전/후)
• 스프링 컨텍스트(context 영역) 내부에서 Controller(Handler)에 관한 요청과 응답에 대해 처리
• 스프링의 모든 빈 객체에서 접근 가능
•인터셉터 여러개 사용가능(로그인, 권한, 로그 등)
• preHandler() : 컨트롤러 메서드 실행전
• postHandler() : 컨트롤러 메서드 실행후 view 페이지 렌더링 전
• afterCompletion() : view 페이지가 렌더링 된 후
6.4 스프링 프레임워크 동작 순서
• HTTP 요청 → 핸들러 조회 →핸들러 어댑터 조회 →핸들러 어댑터 실행 →핸들러 실행 →ModelAndView 반환→viewResolver 호출 →View 반환 →뷰 렌더링 → HTML 응답
※ 스프링 프레임워크 단순 버전
• HTTP 요청 → DispatcherServlet에서 매핑할 컨트롤러가 있는지 HandlerMapping에서 조회 → controller 실행 → 요청 처리 후 결과를 출력한 view 이름을 DispatcherSerlvet에 리턴 → 컨트롤러에 보내온 View이름을 ViewResolver에게 전달 → 처리결과를 View에 송신 → Dispatcher서블릿에서 클라이언트에게 최종 결과 출력
7. @Bean이 무엇인지, new 연산자를 통해 오브젝트를 생성하는것과 무슨 차이가 있는지
•@Bean
- 스프링 컨테이너가 관리하는 자바 객체
- Bean의 등록 방법
: @Component 에노테이션을 사용하면 컴포넌트 스캔이 자동으로 이루어짐
: @Configuration에 직접 @Bean 어노테이션 사용
• new 연산자와 @Bean을 주입 받는것의 차이점
- new 연산자는 실행되는 시점에 인스턴스를 생성
- @Bean은 @Bean에 등록된 이미 존재하는 인스턴스를 프로그램에 맞도록 주입 시켜줌
- @Bean 등록 시 싱글톤으로 관리되어 같은 객체 주소를 참조함
※ 클래스간 결합도를 낮추고 응집도를 높임
8. 싱글톤
8.1 싱클톤이란
•객체의 인스턴스가 오직 한개만 생성되는 디자인 패턴
• 메모리낭비 방지, 속도가 빠름, 데이터 공유가 쉬움
8.2 싱글톤의 단점
• 싱글톤인스턴스가 너무 많은 일을 하거나 많은 데이터를 공유시키면 다른 클래스간의 결합도가 높아짐
• 개방 폐쇄 원칙에 위배(확장에는 열려있고 수정에는 닫혀있는 원칙)
8.3 스프링에서 싱글톤
• 스프링 컨테이너는 객체 인스턴스를 싱글톤으로 관리
• 객체 인스턴스를 공유하기 때문에 객체 상태를 유지하게 설계하면 안됨
9. Spring과 Spring Boot의 차이점
스프링 프레임워크
스프링 부트
Dependency
•dependency를 설정해줄 때 설정 파일이 매우 길고, 모든 dependency에 대해 버전 관리도 하나하나 해줘야 함
•dependency를 Spring Framework보다 쉽게 설정해 줄 수 있고, 라이브러리간 버전 관리도 자동으로 해줌
AutoConfiguration
configuration설정을 할 때 매우 길고, 모든 어노테이션 및 빈 등록 등을 설정해 줘야함
Tomcat 이나 Jetty 같은 내장 WAS를 가지고 있기 때문에 jar 파일로 간편하게 배포
※ 스프링부트의 단점: 대부분 만들어져있고 확장이 가능하기 때문에 소스를 분석하거나 로직을 이해할 때 깊숙히 파고들어야함
10. Spring data JPA
10.1 영속성 컨텍스트란? •객체지향 적인 코딩이 가능 •컬럼 추가시 쿼리를 작성할 필요없이 필드만 추가하면 됨 •특징 : 영속성 컨텍스트라는 것이 존재함으로 인해 발생하는 특징들이 생김 •같은 키로 조회하면 항상 동일한 오브젝트가 리턴되는것이 보장됨 •더티체킹, 엔티티의 변경된 부분만 찾아서 업데이트 함 •지연된 쓰기 지연, Create, Update, Delete시 쿼리를 캐시에 저장하고 있다가 한번에 처리함
•인터페이스 : 모든 메서드가 추상 메서드로 정의 되어있어야 함, 인터페이스를 구현한 객체들은 같은 메서드가 있다는 보장
5. 자바 에플리케이션 실행 과정
•자바 소스 코드 : 개발자가 Java로 작성한 소스 파일로 확장자 명은 .java
•자바 바이트 코드 : 클래스코드가 읽을 수 있도록 컴파일된 파일 확장자명은 .class
•Methode Area : 클래스가 사용되면 클래스별로 클래스 정보가 저장되는 영역(static 변수, static 메소드 등)
•Heap Area : 인스턴스가 생성되는 영역 (Garbage Collection의 대상이 되는 영역)
•Stack Area : 메서드 실행에 필요한 메모리 공간(매개변수, 지역변수, 리턴정보등 을 저장)
•PC Register : 현재 수행중인 JVM 명령어가 저장
•Native Method Area : 자바 외의 언어 (C, C++ 등)를 수행하기위한 영역
6. 가비지 컬렉션(Garbage Collection)
• JVM에서 메모리를 관리해주는 모듈
• Heap 메모리를 재활용 하기 위해 더이상 참조하지 않는 인스턴스들을 메모리에서 제거
• 개발자가 직접 메모리를 정리하지 않아도 됨
• 참조 되지 않는 객체를 찾는 과정에서 Mark and Sweep이 발생하여 스레드가 잠깐 멈춤
7. 객체 지향 프로그래밍
•현실 세계의 사물과 같은 객체를 만들고, 객체에 필요한 특징을 뽑아 프로그래밍을 수행
• 특징
- 추상화 : 객체들의 공통적인 특징(기능, 속성)을 도출하는 것
- 캡슐화 : 실제 구현되는 부분을 외부에 드러나지 않도록 은닉
객체가 독립적으로 역할을 수행하도록 데이터를 하나로 묶어 관리 (응집도가 높아짐)
데이터를 보이지 않고 외부와 상호작용을 할 때 메서드를 이용 (결합도가 낮아짐)
- 상속성 : 하나의 클래스가 가진 특징(함수, 데이터)을 다른 클래스가 그대로 물려받는것
- 다형성 : 약간 다른 방법으로 동작하는 함수를 동일한 이름으로 호출하는것 (오버로딩, 오버라이딩)
•좋은 객체 지향 설계의 5가지 원칙(SOLID) - SRP 단일 책임 원칙 : 한 클래스는 하나의 책임만 가져야한다. 변경이 있을때 파급효과가 적으면 단일 책임 원칙을 잘 따른것 (객체의 생성과 사용을 분리) ※ 책임의 범위를 잘 조절해야함
- OCP 개방 폐쇄 원칙 : 소프트웨어 요소는 확장에는 열려있으나 변경에는 닫혀있어야한다. (다형성) 역할과 구현을 분리, 인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현하는것.
- LSP 리스코프 치환 원칙 : 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야한다. 다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 함. 컴파일은 잘 되더라도 규약을 위배하면 LSP 위반. 자동차 인터페이스의 엑셀은 앞으로가라는 기능, 뒤로가게 구현하게되면 LSP 위반, 느리더라도 앞으로가야함.
- ISP 인터페이스 분리 원칙 : 특정 클라이언트를 위한 인터페이스 여러개가 범용 인터페이스 하나보다 낫다. 자동차 인터페이스 -> 운전 인터페이스, 정비 인터페이스로 분리 사용자 클라이언트 -> 운전자 클라이언트, 정비사 클라이언트로 분리 정비 인터페이스 자체가 변해도 운전자 클라이언트에 영향을 주지 않음 인터페이스가 명확해지고, 대체 가능성이 높아짐
- DIP 의존 관계 역전 원칙 : 프로그래머는 추상화에 의존해야지, 구체화에 의존하면 안된다. 스프링의 의존성 주입은 이 원칙을 따르는 방법중에 하나. 클라이언트 코드가 구현 클래스를 바라보지 말고 인터페이스를 바라보라는 뜻. 역할에 의존해야지 구현에 의존하지 말라는것. 의존 = 내가 그 코드를 안다.
8. 컬렉션 프레임 워크
8.2 Java 컬렉션 프레임워크 구조도
데이터 군을 저장하는 클래스를 표준화 한 설계
List
•순서가 있는 데이터 집합, 데이터의 중복을 허용함 •ArrayList, LinkedList, Stack, Vector
Set
• 순서를 유지 하지 않는 데이터 집합, 데이터 중복을 허용하지 않음 • HashSet, TreeSet
Map
• 키와 값의 쌍으로 이루어진 데이터 집합, 순서유지가 안됨, 키는 중복이 불가, 값은 중복이 가능 • HashMap, TreeMap, HashTable
8.2 List
• 순서가 있고 중복을 허용, 인덱스로 원소에 접근이 가능, 크기가 가변적
• ArrayList
◦ 배열을 기반으로 데이터를 저장
◦ 단반향 포인터 구조로 데이터 순차적 접근에 강점
◦데이터 삽입, 삭제에 불리하나, 순차적 추가/삭제는 빠름
◦ 임의의 요소에 대한 접근성이 뛰어남
•LinkedList
◦연결 기반으로 데이터를 저장
◦각 요소(node)들은 자신과 연결된 다음요소에 대한 참조 주소값과 데이터로 구성되어있음
◦양방향 포인터 구조로 데이터 삽입, 삭제가 빠름
◦임의의 요소에 대한 접근성이 안좋음
8.3 Set
•순서가 없고 중복된 데이터를 허용하지 않음, 중복되지 않은 데이터를 구할 때 유용, 빠른 검색 속도를 가짐
•HashSet
◦인스턴스의 해시값을 기준으로 저장하기 때문에 순서를 보장하지 않음
◦NULL 값을 허용 ◦TreeSet보다 삽입, 삭제가 빠름
•LinkedHashSet
◦ 입력된 순서를 보장
•TreeSet
◦ 이진 탐색 트리(Red-Black Tree)를 기반으로 함 ◦데이터들이 오름차순으로 정렬 ◦데이터 삽입, 삭제에는 시간이 걸리지만 검색, 정렬이 빠름
8.4 Map
• Key와 Value의 한쌍으로 이루어지는 데이터의 집합. Key에 대한 중복이 없으며 순서를 보장하지 않음, 뛰어난 검색 속도
•HashMap ◦Key에 대한 중복이 없으며 순서를 보장하지 않음 ◦Key와 Value 값으로 NULL을 허용 ◦동기화가 보장되지 않음 ◦검색에 가장 뛰어난 성능을 가짐 •HashTable ◦동기화가 보장되어 병렬 프로그래밍이 가능하고 HashMap 보다 처리속도가 느림 ◦Key와 Value 값으로 NULL을 허용하지 않음 •LinkedHashMap ◦입력된 순서를 보장 •TreeMap ◦이진 탐색 트리(Red-Black Tree)를 기반으로 키와 값을 저장 ◦Key 값을 기준으로 오름차순 정렬되고 빠른 검색이 가능 ◦저장 시 정렬을 하기 때문에 시간이 다소 오래 걸림
9. HashTable
※ 해시함수(key) → 해시코드 → Index → Value
• 검색하고자하는 key 값을 입력 받아 해시함수로 로직을 수행
• 반환된 해시코드를 배열의 Index로 환산하여 데이터에 접근 하는 자료구조
10. String, StringBuffer, StringBuilder
10.1 String VS StringBuffer, StringBuilder
• String : 불변의 속성을 가짐
// ① 메모리 할당
String str = new String("hello");
// ② 기존 메모리가 아닌 새로운 메모리에 할당
str = str+" world";
•StringBuffer, StringBuilder
// ① 메모리할당
StringBuffer sb = new StringBuffer("hello");
// ② 기존 메모리에 append
sb.append(" world");
10.2 StringBuffer VS StringBuilder
•StringBuffer : 동기화 키워드를 지원하여 멀티스레드 환경에서 안전함
•StringBuilder : 동기화 지원이 안되지만 단일스레드 환경에서 성능이 좋음
11. 제네릭스(Generics)
•다양한 타입의 객체들을 다루는 메서드나 컬렉션에 컴파일 시 타입을 체크해주는 기능(JDK 1.5에 도입)
•타입의 안정성 제공
•타입체크와 형변환을 생략할 수 있어 코드가 간결해짐
12. CheckedException과 UnCheckedException
CheckedException
UnCheckedException
처리여부
개발 시 반드시 예외를 처리해야함
예외 처리를 강제 하지 않음
확인시점
컴파일 단계
실행단계(Run Time)
예외 종류
RunTimeExcepion을 제외한 모든 예외 SQLException IoException
RunTimeException 하위 예외 NullPointException IndexOutOfBoundException
13. int와 double, Integer와 Double등의 래퍼 타입과 Primitive 타입의 차이점
•Primitive 타입은 변수에 값자체를 저장하지만 래퍼타입은 변수에 객체의 주소 값을 저장
• int에는 null을 넣을수 없지만 Integer는 객체이기 때문에 null 입력이 가능
• Boxing : Primitive타입을 래퍼타입으로 바꾸는것
• UnBoxing : 래퍼타입을 Primitive타입으로 바꾸는것
14. 익명 클래스와 람다 표현식의 차이
•익명 내부 클래스는 새로운 클래스를 생성하지만, 람다는 새로운 메서드를 생성하여 포함. •내부 클래스는 새로운 클래스파일이 생성 •람다는 static 이든, 객체 사용을 위한 non-static 이든, 메서드로 생성. •익명 내부 클래스의 this : 새로 생성된 클래스, 람다의 this : 람다식을 포함하는 클래스
• 람다표현식이 클래스 정의와 구현을 동시에 하여 코드가 더 간결함
• 프로그램 내에서 한번 만 객체로 만드는데 클래스를 정의하고 생성하는 것이 비효율적
15. 스레드를 생성하는 방법
•Runnable 인터페이스를 Implements 하여 run 메서드를 정의
• Thread 클래스를 상속 받아 run 메서드를 오버라이딩
JSP
1. Servlet과 JSP, MVC
• Servlet
- 자바 소스코드에서 response로 PrintWriter객체에 HTML 소스를 삽입하여 response를 함
- 웹페이지를 동적으로 생성하기 위한 서버측 프로그램
- 자바를 기반으로 만들어지면 WAS위에서 컴파일되고 동작함
• JSP
- JavaServerPage의 약자로 HTML 소스에 스클립틀릿(<% ... %>)에 자바소스를 작성
- 웹페이지를 동적으로 생성하기 위한 웹 어플리케이션 도구
- Java 기반의 서버사이드 스크립트 언어
※ 스크립트 언어란 컴파일 없이 내장된 번역기로 실행할 수 있는 프로그래밍언어
- JSP 컨테이너에서 서블릿을 생성하여 컴파일 후 실행하는 구조
• MVC
- Model View Controller : 하나의 서블릿이나, JSP로 처리하던 것을 컨트롤러(Controller)와 뷰(View)라는 영역으로 서로 역할을 나눈 것
- 컨트롤러:HTTP 요청을 받아서 파라미터를 검증하고, 비즈니스 로직을 실행. 뷰에 전달할 결과 데이터를 조회해서 모델에 담음.
- 모델:뷰에 출력할 데이터를 담아서 뷰에 전달함. 따라서 뷰는 비즈니스 로직이나 데이터 접근을 몰라도 되고, 화면을 렌더링 하는 일에 집중할 수 있음
- 뷰:모델에 담겨있는 데이터를 사용해서 화면을 그리는 일에 집중. (HTML을 생성하는 부분)
• 데이터베이스 : 관계형 데이터 (ex: [Oracle, Mysql, PostgreSQL...])
•객체를 관계형 DB에 관리하는 것에 시간을 많이씀
• 객체 → 데이터, 데이터 → 객체 : SQL 중심적인 개발이 됨
2. SQL 중심적인 개발의 문제점
2.1 기능추가, 테이블 생성 될때마다 CRUD SQL을 다 만들어야함
(JdbcTemplate, MyBatis가 Mapping에 도움을 주는 것은 있지만 그래도 한계가 있음)
•회원 객체의 CRUD가 구현되있는 상황
- 기존 회원 객체 테이블 기능 쿼리 구현
/*회원 객체*/
public class Member {
private String memberId;
private String name;
}
/*쿼리*/
INSERT INTO MEMBER(MEMBER_ID, NAME) VALUES ...
SELECT MEMBER_ID, NAME FROM MEMBER M
UPDATE MEMBER SET .
- 전화 번호 필드를 추가해야하는 상황
/*회원 객체*/
public class Member {
private String memberId;
private String name;
private String tel;
}
/*쿼리*/
INSERT INTO MEMBER(MEMBER_ID, NAME, TEL) VALUES ...
SELECT MEMBER_ID, NAME, TEL FROM MEMBER M
UPDATE MEMBER SET ...TEL = ?
※ 모든 CRUD에 TEL을 하나하나 추가해야함
※ SQL에 의존적인 개발
2.2 패러다임의 불일치 (객체 VS RDBMS)
• 관계형 데이터베이스와 객체지향은 사상이 다름 객체지향개발 : 추상화, 캡슐화, 정보은닉, 상속, 다형성등 시스템의 복잡성을 제어하는 다양한 장치들 제공 관계형데이터베이스 : 데이터를 잘 정규화해서 저장하는 것이 목표.
•객체를 관계형 데이터베이스에 저장하는 도식
※ 객체를 SQL로 변환해서 RDB에 저장하는 변환과정을 개발자가 해야함
3. 객체와 관계형 데이터 베이스의 차이
3.1 상속
•객체의 상속관계와 유사한 관계형 데이터베이스의 개념으로 Table 슈퍼타입, 서브타입 관계가 있음 •상속 받은 객체(Album, Movie, Book)을 데이터베이스에 저장하려면 복잡함 - 객체 분해 : Album객체를 Item과 분리
- 조회한 필드를 각각 맞는 객체(ITEM, ALBUM)에 매핑시켜서 가져와야함 결론: DB에 저장할 객체는 상속관계를 쓰지 않음
3.2 연관 관계
•객체는 참조를 사용 : member.getTeam(); •테이블은 외래 키를 사용 : JOIN ON M.TEAM_ID = T.TEAM_ID
• Member와 Team간에 참조 - 객체: Member → Team 은 member.getTeam()을 통해 가능, Team → Member 는 참조할 객체가 없기 때문에 불가능 - 테이블: 서로가 서로를 참조할 키(FK)가 있기 때문에 양측이 참조가 가능하다. Member ↔ Team
• 객체를 테이블에 맞춰 모델링, 테이블에 맞춘 객체 저장
/* 회원 객체 */
class Member{
String id; //MEMBER_ID 컬럼 사용
Long teamId; //참조로 연관관계를 맺는다.
String username; // USERNAME 컬럼 사용
}
/* 팀객체 */
class Team{
Long id; //TEAM_ID 컬럼 사용
String name; //NAME 컬럼 사용
}
/* 쿼리 */
INSERT INTO MEMBER(MEMBER_ID, TEAM_ID, USERNAME) VALUES ...
INSERT INTO TEAM(TEAM_ID, NAME) VALUES...
- 객체 지향적이지 못함
•객체다운 모델링, 객체 모델링 저장
/* 회원 객체 */
class Member{
String id; // MEMBER_ID 컬럼 사용
Team team; // Team 객체 참조로 연관관계를 맺음
String username; // USERNAME 컬럼 사용
Team getTeam(){
return team;
}
}
/* 팀객체 */
class Team{
Long id; // TEAM_ID 컬럼 사용
String name; // NAME 컬럼 사용
}
/* 쿼리에 teamId를 저장하기 위해 꺼냄*/
member.getTeam().getId();
/*쿼리*/
INSERT INTO MEMBER(MEMBER_ID, TEAM_ID, USERNAME) VALUES ...
INSERT INTO TEAM(TEAM_ID, NAME) VALUES...
•객체 모델링 조회
SELECT M.*, T.*
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
public Member find(String memberId){
//SQL 실행
Member member = new Member();
//데이터터베이스에서 조회한 회원 관련 정보를 모두 입력
Team team = new Team();
//회원과 팀 관계 설정
member.setTeam(team);
return member;
}
SELECT M.*, T.*
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
member.getTeam(); //OK
member.getOrder(); //null
class MemberService{
...
public void process(){
Member member = memberDao.find(memberId);
member.getTeam(); //????
member.getOrder().getDelivery(); //????
}
}
※ 엔티티 신뢰 문제가 발생
- member안에 모든 참조를 자유롭게 참조 할 수 없음
- 새로운 필드를 추가했는데 조회 로직에서 해당 부분 매핑을 빼놓을 가능성도 있음 - 모든 코드와 쿼리를 분석해보기전까지는 엔티티 그래프 검색이 어디까지 되는지 확신할 수 없음
String memberId = "100";
Member member1 = memberDAO.getMember(memberId);
Member member2 = memberDAO.getMember(memberId);
member1 == member2; // false 다르다
class MemberDAO{
public Member getMember(String memberId){
String sql = "SELECT * FROM MEMBER WHERE MEMBER_ID = ?";
...
//JDBC API, SQL 실행
return new Member(...);
}
}
- member를 조회할 때마다 new Member()를 통해 새로운 객체를 만들어서 조회 하기 때문에 두 인스턴스 비교는 내용물이 같더라도 참조값이 다름
• 자바 컬렉션에서 조회한 객체 비교
String memberId = "100";
Member member1 = list.get(memberId);
Member member2 = list.get(memberId);
member1 == member2; // true 같다.
※ 동일성과 동등성(Identical & Equality) - 자바에서 두 개의 오브젝트 혹은 값이 같은가 : 동일한 오브젝트라는 말(identical)은 같은 레퍼런스를 바라보고 있는 객체라는 말로 실제론 하나의 오브젝트라는 의미
: 동등한 오브젝트라는 말(Equivalent)은 같은 내용을 담고 있는 객체라는 의미
※ 결론
객체지향적으로 모델링을 할 수록 매핑작업만 늘어나고 불일치가 늘어나서 사이드이펙트가 커지기만함. 객체를 자바 컬렉션에 저장하듯이 DB에 저장하기위해 나온 것이 JPA
JPA (Java Persistence API)
1. 용어
•JPA : 자바진영의 ORM 기술 표준
•ORM
- Object-relational mapping(객체 관계 매핑) - 객체는 객체대로 설계 하고, RDBMS는 RDBMS대로 설계해서 ORM 프레임워크가 중간에서 매핑 해줌 - 대중적인 언어에는 대부분 ORM 기술이 존재
2. JPA 동작 원리
2.1 애플리케이션과 JDBC 사이에서 동작함
2.2 저장
2.3 조회
3. JPA를 사용하는 이유
3.1 생산성
• 저장: jpa.persist(member) • 조회: Member member = jpa.find(memberId) • 수정: member.setName(“변경할 이름”) • 삭제: jpa.remove(member)
※ CRUD가 간편함
3.2 유지보수
/*회원 객체*/
public class Member {
private String memberId;
private String name;
private String tel;
}
/*쿼리*/
INSERT INTO MEMBER(MEMBER_ID, NAME, TEL) VALUES ...
SELECT MEMBER_ID, NAME, TEL FROM MEMBER M
UPDATE MEMBER SET ...TEL = ?
※ 기존에는 필드 변경시 모든 SQL을 수정했어야했으나, JPA는 필드만 추가하면 되고 SQL은 JPA가 수행함
3.3 패러다임 불일치 해결
• 상속
- 상속되어 있는 객체의 저장
: 개발자의 할일 jpa.persist(album);
: JPA가 나머지 처리
- 상속되어 있는 객체의 조회
: 개발자가 할일 Album album = jpa.find(Album.class, albumId);
: JPA가 나머지 처리
• 연관관계
- 연관관계 저장
member.setTeam(team);
jpa.persist(member);
• 객체 그래프 탐색
Member member = jpa.find(Member.class, memberId);
Team team = member.getTeam()
※ JPA로 계층 엔티티를 신뢰할수 있음
• 비교
String memberId = "100";
Member member1 = jpa.find(Member.class, memberId);
Member member2 = jpa.find(Member.class, memberId);
member1 == member2; //같다
※ 동일한 트랜잭션에서 조회한 엔티티는 같음을 보장
3.4 성능 최적화 기능
•1차 캐시와 동일성 보장
String memberId = "100";
Member m1 = jpa.find(Member.class, memberId); //SQL
Member m2 = jpa.find(Member.class, memberId); //캐시
println(m1 == m2) //true
- 같은 트렌젝션 안에는 같은 엔티티를 반환(조회성능 향상)
- DB Isolation Level이 Read Committed이어도 애플리케이션에서 Repeatable Read 보장
- SQL을 한번만 수행함
※ DB Isolation Level(DB 격리 수준)
<아래로 내려갈수록 트랜잭션간 고립 정도가 높아지고 성능이 떨어짐>
- READ UNCOMMITTED : 어떤 트랜잭션의 변경내용이 COMMIT이나 ROLLBACK과 상관없이 다른 트랜잭션에서 보여짐
- READ COMMITTED: 어떤 트랜잭션의 변경 내용이 COMMIT 되어야만 다른 트랜잭션에서 조회할 수 있음
- REPEATABLE READ : 트랜잭션이 시작되기 전에 커밋된 내용에 대해서만 조회할 수 있는 격리수준
- SERIALIZABLE : 읽기 작업에도 공유 잠금 설정, 동시에 다른 트랜잭션에서 이 레코드를 변경하지 못하게 됨
• 쓰기 지연
- insert
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 모아서 보낸다.
transaction.commit(); // [트랜잭션] 커밋
※ 트랜잭션을 커밋할 때까지 INSERT SQL을 모으고 JDBC BATCH SQL 기능을 사용해서 한번에 SQL 전송
- update
transaction.begin(); // [트랜잭션] 시작
changeMember(memberA);
deleteMember(memberB);
비즈니스_로직_수행(); //비즈니스 로직 수행 동안 DB 로우 락이 걸리지 않는다.
//커밋하는 순간 데이터베이스에 UPDATE, DELETE SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋
※ UPDATE, DELETE로 인한 로우(ROW)락 시간 최소화 ※ 트랜잭션 커밋 시 UPDATE, DELETE SQL 실행하고, 바로 커밋
• 지연 로딩
- 지연 로딩: 객체가 실제 사용될 때 로딩 - 즉시 로딩: JOIN SQL로 한번에 연관된 객체까지 미리 조회