java
나만의 인터뷰 질문
SpringMVC 와 SpringBoot 차이점이 무엇인가요?
- 스프링 MVC는 HTTP 요청 및 응답을 단순화하는 SPRING 프레임워크 내의 라이브러리입니다. ServletAPI를 기반으로하며 MVC는 핵심기능인 Model View Controller의 약자로 각 로직들을 분리할 수 있습니다.
- 스프링 부트는 자동구성을 사용하여 웹 어플리케이션을 빌드하는데 필요한 프로세스를 구성할 수 있습니다. 별도의 서버 설치를 필요 없이 내부 임베디드 톰켓을 사용하여 실행할 수 있습니다.
- 차이점은 스프링 MVC를 사용할 때, 컴포넌트 스캔, 디스패처 서블릿, 뷰리졸버, jar를 설정해줘야합니다. 하지만 스프링 부트는 Springmvc. jar를 통해 자동 구성을 해줍니다.
- 스프링 MVC, Logging, Jackson-databind등 스프링 MVC버전에 호환되는 것들을 선택해야 했지만 스프링 부트에서는 spring-mvc-starter-web 의존성을 추가해주면 필요한 것들을 기본으로 추가해줍니다.
- 스프링 부트와 MVC를 프로젝트로 각각 진행했을 때, MVC에서 설정이 오래걸렸던 부분들을 스프링 부트로 사용함으로써 단순해졌고, 버전에 대해 고려해야할 점을 없애 빠르게 설정을 해줘서 좋았습니다.
Immutable이 무엇인가요?
- 객체지향에 프로그래밍에 있어 불변은 생성 후 그 객체를 변경할 수 없는 것입니다.
- 불변이 중요한 이유는 외부에서 객체를 변경할 경우 상태 변화를 추적하기 힘들기 때문에 상태변화에 따른 것을 막는것이 중요합니다.
- 자바에서는 Immutable 객체를 final로 선언하여 사용합니다.
- 정적 팩토리 메서드를 추가하여 외부에서 생성자를 자유롭게 호출되는 것을 방어할 수 있습니다.
- 이렇게 함으로써 다른 사람이 호출하더라도 값이 값이 변하지 않는다는 것을 확신할 수 있습니다.
자바 제네릭이 무엇인가요?
- 제네릭은 클래스 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법입니다.
- 자바에서 다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 컴파일 시 타입체크를 해주고, 중복 되는 코드를 제거해줄 수 있습니다.
- 제네릭을 사용하면서 사용 가능한 타입을 검증하고 사용 가능하지 않은 타입은 프로그램이 실행되기 전 컴파일 시점에 오류가 발생해 개발자에게 편리한 기능이라고 생각합니다.
인터페이스란 무엇인가요?
- 클래스들이 구현해야 하는 동작을 지정하는데 사용하는 추상 자료형입니다.
- 인터페이스를 사용할 때 상속을 해야합니다.
- 인터페이스를 사용함으로써, 확장성이 좋은 코드를 작성할 수 있고, 결합도가 강하지 않은 프로그래밍을 할 수 있습니다.
Singleton이 무엇인가요?
- 객체의 인스턴스가 오직 하나 1개만 생성하는 것입니다.
- 인스턴스를 오직 하나만 존재한다면, 메모리 측면에서 낭비를 줄일 수 있고, 전역으로 사용되는 인스턴스이기 때문에 데이터 공유가 쉽습니다.
- 요청이 많은 곳에서 사용하면 한 개의 인스턴스만 공유되기 때문에 효율성이 좋습니다
- 하지만 싱글톤 객체 사용유무와 상관없이 클래스 로딩되는 시점에 항상 싱글톤 객체가 생성되고, 메모리를 잡고 있기 때문에 비효율적일 수도 있습니다.
public class Singleton { private static Singleton instance = new Singleton(); private Singleton(){} public static Singleton getInstance() { if (istance == null) { instance = new Singleton(); } return instance; } }
Spring Security 동작원리를 설명해주세요
- 먼저 스프링 시큐리티는 스프링기반의 어플리케이션 보안을 담당하는 스프링 하위 프레임워크입니다.
- 인증과 권한에 대한 부분을 필터 흐름에 따라 처리하고 있습니다.
- 로그인 요청이 오면 AuthenticationFilter가 HttpServletRequest의 아이디와 비밀번호를 가로챕니다.
- AuthenticationManager에게 UserPasswordAuthenticationToken을 위임합니다.
- 실제 인증을 할 AuthenticationProvider에게 Authentication(UserPasswordAuthenticationToken) 객체를 다시 전달합니다.
- DB에서 사용자 인증 정보를 가져올 UserDetailsService 객체에게 사용자 아이디와 암호화 된 비밀번호를 넘겨주고, UserDetails 객체를 전달받습니다.
- AuthenticationProvider는 UserDetails 객체를 전달 받은 후 사용자 입력정보와 UserDetails를 가지고 인증을 시도합니다.
- 인증이 완료되면 SecurityContextHolder에 담은 후 AuthenticationSuccessHandle을 실행합니다.
- 시큐리티를 쓰면서 요청별 시큐리티에 대한 정보를 가질 수 있어 DB에 저장하는 것보다 더 편리하다고 생각이 들었습니다.
다형성이란 무엇인가요?
- 하나의 타입에 여러 객체를 대입할 수 있는 것입니다.
- 다형성을 활용하면 기능을 확장하거나, 객체를 변경해야할 때 타입 변경없이 객체 주입으로 수정을 할 수 있습니다. 상속을 같이 활용한다면 중복되는 코드를 제거할 수 있으므로 객체지향 설계와 가까워 질 수 있다고 생각합니다.
- 다형성 중 오버라이딩을 사용할 경우에 if-else 분기가 늘어나는 것을 제거 할 수 있고, 코드를 수정하는것이 아닌 추가하는 것으로 기존 코드를 건드리지 않을 수 있습니다.
- 다형성을 잘 활용한다면 코드의 중복을 줄이면서, 변경과 확장에 유연한 객체지향 코드를 작성하는데 유용합니다.
REST API가 무엇인가요?
- 자원을 이름으로 구분하고 정보를 주고받는 것 입니다.
- 현재는 다양한 클라이언트가 등장하기 때문에 JSON을 통해 데이터를 주고 받는다면 어떤 클라이언트가 와도 대응할 수 있습니다.
- 장점은 rest api를 읽는 것만으로도 무엇을 하는지 알 수 있으며, STATELESS 이기 때문에 각자의 역할이 분리되어 있습니다.
- 단점은 표준이 잘 정해져있지 않아 다양한 개발자들이 사용할 때 컨벤션이 지켜지기 어려울 수 있습니다.
객체지향 프로그래밍
- 각 객체마다 자신의 책임과 역할을 온전히 다하며, 서로 협력하여 변경에 유연한 프로그래밍을 하는기법. 즉, 각 객체마다 올바른 캡슐화를 통해 응집도를 높이고 낮은 결합도를 이루어나는 것
- 캡슐화 : 변경될 수 있는 부분은 최대한 외부에 노출시키지 않고 협력에 의한 인터페이스만으로 소통을 하는 것
- 메시지를 통해 그 객체가 자기 일을 처리한다.
DI / IOC가 무엇인가요?
- IoC
- 객체의 흐름, 생명주기등 독립적인 제 3자에게 역할과 책임을 위임하는 방식
- 개발자가 작성하고자 하는 코드에 변경에 유연한 코드구조를 가져가기 위해 나타났다.
- 수 많은 객체를 편리하게 관리할 수 있다.
- 객체를 관리해주는 독립적인 존재와 그 외 개발자가 구현하고자 하는 부분으로 각각 관심을 분리하고 서로에 역할을 충실히하며 변경에 유연한 코드를 작성할 수 있는 구조이기 때문에 제어를 역전하였다.
- DI
- 인터페이스를 통해 다이나믹하게 객체를 주입하여 유연한 프로그래밍을 해주는 방식
- 의존관계에 있는 객체들이 있을 때, 외부에서 객체에 레퍼런스를 전달하여 사용하고자 하는 객체에서 코드를 작성할 수 있게한다.
mybatis
의 역할이 무엇인가요?
- 마이바티스는 프록그램 소스코드에서 SQL문장을 별도로 관리하여 XML로 저장하여 JDBC 코드의 작성의 불편함을 없애줍니다.
- JSP와 서블릿 기반으로 프로젝트를 진행했었는대, JDBC 코드의 양이 너무많고 커넥션 관리를 메서드마다 해주는 불편함이 있었는대, 마이 바티스를 사용함으로써 쿼리 그대로 작성하며 유지보수하기와 한 눈에 보기 좋았습니다.
Reflection이란
- 자바에서 이미 로딩이 완료된 클래스에서 또 다른 클래스를 동적으로 로딩하여 생성자, 맴버필드 등을 사용할 수 있는 기법
- 클래스의 패키지 정보, 접근제어자, 어노테이션을 얻을 수 있음
- 컴파일 시점이 아니라 실행시점에 동적으로 클래스의 정보를 객체화를 통해 분석
- 사용하는 이유: 실행시간에 다른 클래스를 동적으로 로딩하여 접근할 때, 클래스와 멤버 필드 그리고 메서드 등에 관한 정보를 얻어야할 떄
HTTP 통신
- Transport layer
- 신뢰성(데이터를 순차적, 안정적인 전달)
- 전송(포트 번호에 해당하는 프로세스에 데이터 전달)
- TCP
- 신뢰성있는 데이터 통신을 가능하게 해주는 프로토콜
- Connection 연결(양방향 통신) 3 way handshake
- 연결 신청(Syn 비트를 1로 설정해 패킷송신)
- Syn, Ack 비트를 1로 설정해 패킷송신
- Ack 비트를 1로 설정해 패킷 송신
- 연결이 됨
- 패킷 보냄
- ack 응답
- ack를 수신하지 못하면 재전송
- 혼잡 제어
- 오류 감지
- TCP Header
- 문제점
- 신뢰성 보장하지만 매번 Connection을 연결하여 시간 손실 발생
- 패킷을 조금만 손실해도 재전송
- UDP
- TCP 보다 신뢰성이 떨어지지만 전송 속도가 일반적으로 빠른 프로토콜
- Connectionless
- 에러검출
- 비교적 데이터의 신뢰성이 중요하지 않을때 사용(영상 스트리밍)
JVM
- 자바 바이트코드는 타켓 플램폼에 상관없이 JVM 위에서 동작 - Write Once, Run Anywhere - 내부 구조 - runtime data areas - method area, heap -> 스레드 공유 - 클래스로더가 클래스 파일을 읽어오면 클래스 정보를 파싱해서 method area에 저장 - 프로그램을 실행하면서 생성된 모든 객체를 heap에 저장 - java stacks, pc registers, native method stack - pc registers - 각 스레드는 메서드를 실행하고 있고, pc는 그 메서드 안에서 바이트드 몇번 째 줄을 실행하고 있는지 알려줌 - stack - 자바 스택은 스레드별로 1개만 존재하고, 스택 프레임은 호출 될때마다 생성된다. 메서드 실행이 끝나면 스택 프래임은 pop되어 제거된다. - stack frame - 자바 bytecode가 아닌 다른 언어들로 작성된 메서드 - device마다 register 기반으로 만들지 않아 stack을 써서 jvm 운영
Process vs Thread
- 실행 단위 : cpu core에서 실행하는 하나의 단위로 프로세스와 스레드를 포괄하는 개념 - 프로세스 : 하나의 스레드만 가지고 있는 단일 스레드 프로세스 - 동시성 : 한 순간에 여러가지 일이 아니라, 짧은 전환으로 여러가지 일을 동시에 처리하는 것처럼 보이는 것 - 프로세스 - Code : 실행 명력을 포함하는 코드들(스레드에서 공유) - Data : Static 변수 혹은 글로벌 변수(공유) - Heap : 동적 메모리 영역(공유) - Stack: 지역변수, 매개변수, 반환 값 등등 일시적인 데이터 - PCB - Pointer : 대기상태 큐를 구현하기 위한 포인터 - Process state : 현재 프로세스 상태 - PID : 고유번호 - PC : 다음 명령어를 가르키는 - 컨택스트 스위칭 - 모든 자원을 CPU적재 후 다시 모든 자원을 빼고 다른 프로세스의 모든 자원을 적재 후 반복.. - 멀티 프로세스 - 각 프로세스 독립적 - IPC를 사용한 통신 - 자원 소모적, 개별 메모리 차지 - 컨텍스트 스위칭 비용이 큼 - 동기화 작업이 필요하지 않음 - 스레드 - 한 프로세스 내에서 구분되어진 실행단위 - 컨테스트 스위칭 - 필요한 자원들은 공유하여 다른 스레드가 들어왔을 때 공유 - 멀티 스레드 - 공유된 자원으로 통신 비용절감 - 공유된 자원으로 메모리가 효율적 - 컨텍스트 스위칭 비용이 적음 - 공유자원을 관리해줘야함 - 멀티 프로세스를 이용하는 이유 - 각 공유된 자원에 문제가 생기면 다른 자원에도 영향이 간다. - 멀티 프로세스는 독립적으로 실행하기 때문에 영향을 미치지 않는다.
blocking vs non-blocking
- blocking: 자신의 작업을 진행하다가 다른 주체의 작업이 시작이 시작되면 끝날때 까지 기다렸다가 자신의 작업을 시작 - non-blocking: 다른 주체와 상관없이 자신의 작업을 진행 - 다른 주체가 작업을 할 때 자신의 제어권이 있는지 없는지로 볼 수 있다. - Sync vs Async - Sync : 작업을 동시에 수행하거나 동시에 끝나거나, 끝나는 동시에 시작함을 의미 - Async : 시작 종료가 일치하지 않으며, 끝나는 동시에 시작을 하지 않음을 의미 - 순서와 결과의 관점
garbage collector
- 동적으로 할당 된 메모리 영역 중 사용하지 않는 영역을 탐지하여 해제하는 기능 - stack: 정적으로 할당된 메모리 영역 - 원시 타입의 데이터가 값과 함께 할당, Heap영역에 생성된 Object 타입의 데이터의 참조 값 할당 - heap: 동적으로 할당된 메모리 영역 - 모든 Object type의 데이터가 할당, Heap 영역의 Object를 가르키는 참조 변수가 Stack에 할당 - 과정 - 스택의 모든 변수를 스캔하면서 각각 어떤 객체를 참조하고 있는지 찾아서 마킹 - reachable object가 참조하고 있는 객체도 찾아서 마킹(mark) - 마킹되지 않응 객체를 heap에서 제거(sweep) - 언제일어나는지 ? - New Genration 1. Eden - 새로운 객체가 할당(반복) - 가득 차면 GC 발생 (mark and sweep) - unreacahble object가 메모리에서 해제 2. survival 0 - eden에서 살아남은 객체가 이동 - survival1에서 0로 이동 - 가득 차면 mark and sweep - age 값이 특정 값 이상이 되면 Old generation 영역으로 옮겨진다. (promotion) - 반복 3. survival 1 - survival0에서 1로 이동 - 이동한 객체는 age값 증가 - 가득 차면 mark and sweep - age 값이 특정 값 이상이 되면 Old generation 영역으로 옮겨진다. (promotion) - 반복 - 0, 1 영역은 둘 중 하나는 항상 비워있는 상태 - OldGeneration - promotion 과정이 일어나면 GC 발생(Major) - 반복 - Garbage Collector 종류 - Serial GC - 하나의 CPU로 Young 영역과 Old영역을 연속적으로 처리 - 컬렉션이 수행될 때 Stop the world가 발생 - 살아있는 객체는 eden -> eden 꽉차면 servivor 영역으로 이동 -> survivor 영역이 꽉차면 old 영역으로 이동 -> old 영역에서는 쓰지 않는 객체를 한 곳을 모으고 삭제하는 mark sweep compact 알고리즘 사용 - Parallel GC - GC를 처리하는 스레드가 여러개 - GC의 오버헤드를 줄여준다. - 메모리가 충분하고 코어의 개수가 많을 때 사용하면 좋다. - Concurrent Mark Sweep GC - 여러개의 스레드 이용 - 백그라운드에서 쓰레드를 만들어 old 영역에 있는 객체들을 계속 정리 - 메모리 파편화, Cpu 리소스를 많이 잡아먹음 - G1 Collector - 기존 young, old 영역의 개념과 다른 heap 영역에 resion 도입 - 하나 이상의 resion을 도입 - 참조가 없는 객체들은 지우고, 사용중인 객체는 다른 region 고스란히 복사 - 다른 지역으로 사용중인 객체를 차곡차곡 모아 compaction 되므로 파편화 현상이 생기지 않음
RestFul API
- 자원의 이름을 구분하여 해당 자원의 상태를 주고 받는 것 - HTTP 프로토콜을 그대로 활용하기 때문에 웹의 장점을 활용할 수 있는 아키텍처 스타일 - HTTP URI를 통해 Resource를 명시하고, HTTP Method를 통해 자원의 CRUD 기능을 적용하는 것 - 지켜야하는 스타일 - client => server 명확히 역할을 구분지어야함 - stateless => 서버는 클라이언트의 정보를 가지고 있을 필요가 없다. 서버는 오는 요청에만 응답한다. - 캐시 사용이 가능해야한다. - uniform interface => 통일된 인터페이스 - 모든 RestAPi는 공통의 인터페이스를 가져야한다. - 리소스는 URI로 식별 - HTTP 메서드에 표현을 담아야한다. - 메시지 스스로가 어떤 API인지 설명할 수 있어야한다. - 응답에는 CONTENT TYPE이 있어야한다. - HATEOAS => a link로 연결이 되어있어야한다. 게시물 조회 후 수정 등. - 장점 - HTTP 표준 프로토콜을 따르는 모든 플랫폼에서 사용 가능 - 메시지를 의도하는 바를 명확하게 나타내므로 가독성이 좋다. - 서버와 클라이언트의 역할을 명확하게 분리 - 단점 - 표준이 존재하지 않는다. - 특징 - 서버 , 클라이언트 구조 - 자원이 있는 쪽이 서버, 자원을 요청하는 쪽이 클라이언트 - 서로 간 의존성을 줄여준다 - 무상태 - Http Protocal은 무상태이므로 RestAPi도 무상태를 갖는다. - Client의 Context를 서버에 저장하지 않는다. - 서버는 각각의 요청을 완전히 별개로 인식
Filter Interceptor Aop
- 공통점: 공통 부분을 빼서 따로 관리하기 위한 것
- 실행시점
- Filter: 요청과 DispatcherServlet 사이, 응답과 DispatcherServlet 사이(Encoding, 앞 뒤 요청 응답 로그)
- Interceptor: DispatcherServlet과 Controller 사이(DispatcherServlet이 컨트롤러를 호출하기 전과 후 로그인이나 권한 체크)
- AOP: OOP를 보완하기 나온 개념(개방 폐쇄 원칙). 중복이 많으면 모든 곳에서 코드가 변경이 되어야하기 때문에, 핵심기능과 부가기능으로 분리. (트랜잭션, 에러처리)
스프링 vs 노드
- Spring MVC
- 클라이언트 요청 마다 스레드 생성
- 스레드는 요청과 응답이 마무리될 때까지 그 요청을 담당
- 스레드 풀에서 들어온 순서대로 처리
- 장점: 스레드가 여러개라서 많은 요청을 동시에 처리할 수 있다.
- 단점: 너무 많은 요청이 동시에 들어오면 수행시간이 증가.
- Node
- 들어온 요청을 단일 스레드가 처리하늗네, 순차적으로 진행하다가 어떤 순서에서 이벤트가 발생하는 요소가 존재하면 그 일을 대신해주는 이벤트 루프한테 전달해서 그 일은 이벤트 루프가 따로 수행하도록 한다.비동기 처리
빈 스코프
- 빈이란 ?
- 스프링에서 POJO로 불림
- 어플리케이션의 핵심을 이루는 객체이며, 스프링 IoC 컨테이너에 의해 인스턴스화 관리 생성 된다.
- 싱글톤
- spring default
- 스프링 컨테이너의 시작과 종료까지 1개가 유지
- 프로토타입
- 빈의 생성과 의존관계 주입까지만 관여
- 요청이 오면 항상 새로운 인스턴스 생성
- 클라이언트가 관리
- request
- 각가의 요청이 들어오고 나갈 때까지 유지
- session
- 세션이 생성되고 종료될 때 까지
- application
- 웹의 서블릿 컨텍스트와 같은 범위로 유지
빈의 생명주기
- 스프링 컨테이너 생성 -> 스프링 빈 생성 -> 의존관계 주입 -> 초기화 콜백 -> 사용 -> 소멸전 콜백 -> 스프링 종료
- 초기화 콜백: 빈이 생성되고, 빈의 의존관계 주입이 완료된 후 호출
- 소멸전 콜백: 빈이 소멸되기 직전 호출
스프링 레이지로딩
- 스프링의 자바 빈 로딩 - 기본적으로 스프링의 ApplicationContext, WebApplicationContext는 애플리케이션을 시작할 때 모든 싱글톤 빈을 생성하고 초기화 - 대부분의 초기 단계에서 빈의 설정문제를 감지 - Lazy Loading - @Lazy가 정의되어있다면, 다른 참조되는 빈에 의해 사용되거나 실제 참조될 때 로드된다.
스프링 request 처리 과정
- request 요청
- dispatcherServlet에서 handlerMapping 중 requestUrl에 해당하는 handler 검색
- handlerMapping은 컨트롤러 메서드를 지정하고 handlerAdpater가 지정한 메서드를 실행한다.
- 메서드는 request를 처리하고 ModelAndView 객체를 DispatcherServlet에 반환
- 반환 후 인터셉트에 처리할 내용이 있으면 처리
- DispatcherServlet은 ViewResolver를 통해 적합한 View 검색
- DispatcherServlet은 해당 View 응답
스프링 IoC 컨테이거 빈을 등록하는 원리
- 컴포넌트 스캔
- @SpringBootApplication 안에 @Configuration이 들어있고, 컴포넌스 스캔 위치를 알려주는 @ComponentScan이 있다.
- 직접 빈 등록
- @Configuration 붙인 클래스에 @Bean을 등록
- 빈 주입 방법
- 생성자 주입
- 생성자 주입은 객체 생성 시 딱 1번만 호출되기 때문에 불변으로 설계한다.
- 생성자 주입
CORS
- Cross Origin Resource Sharing은 호스트를 가로질러 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 정책
- 네이버에서 get ‘google/post/1’을 호출했을 때, 네이버에서 구글의 리소스 요청은 same origin policy(sop) 정책에 의해 제한된다. 이것은 자신의 출처와 같은 리소스만 불러올 수 있다. 다른 출처의 리소스를 불러오려면 해당 자원에 접근할 수 있는 권한을 부여하도록 브라우저에게 알려줘야한다. 이것이 CORS
Servlet WebApplicationContext vs Root WebApplicationContext
- Application Context
- web application
- 최상단에 위치하고 있는 context
- Spring에서 ApplicationContext란 BeanFactory를 상속받는 context
- Bean에 대한 IoC
- 특정 서블릿 설정과 관계 없는 설정을 한다.(@Service, @Repository, @Configuration ..)
- 서로 다른 여러 Servlet에서 공통적으로 공유해서 사용할 수 있는 Bean을 선언한다.
- Application Context에 정의된 Bean은 Servlet Context 정의 된 Bean을 사용할 수 없다.
- Servlet Context
- servlet 단위로 생성되는 context
- url 설정이 있는 bean을 생성(@Controller, Interceptor)
- Application Context를 자신의 부모 Context로 사용
- Bean 찾는 순서
- ServletContext 먼저 찾는다.
- 망냑에 없다면 Application Context에 정의된 빈을 찾음
- ServletContext에 정의된 빈은 Application context의 빈을 사용할 수 있다.
SOLID 원칙
- SRP(단일책임원칙)
- 작성된 클래스는 하나의 기능만 가지며 클래스가 제공하는 모든 서비스는 그 하나의 책임을 수행해야 하는데 집중되어 있어야한다.
- 어떤 변화에 있어 클래스를 변경해야하는 이유는 오직 하나 뿐이어야 함
- OCP(개방폐쇄원칙)
- 클래스의 구성요소는 확장에는 열려있고, 변경에는 닫혀있어야한다.
- 변경을 위한 비용은 가능한 줄이고 확장을 위한 비용은 가능한 극대화 해야한다는 의미
- OCP를 가능하게 하는 중요 메커니즘은 추상화, 다형성
- LSP(리스코프 치환 원칙)
- 서브타입은 언제나 수퍼 타입으로 교체할 수 있어야한다.
- ISP(인터페이스 분리 원칙)
- 클래스는 자신이 사용하지 않는 인터페이스는 구현하지 말아야한다.
- 어떤 클래스가 다른 클래스에 종속될 때 가능한 최소한의 인터페이스만을 사용해야한다.
- DIP(의존역전원칙)
- 고차원 모듈은 저차원 모듈에 의존하면 안된다. 두 모듈 모두 추상화 된것에 의존해야한다.
- 추상화 된것은 구체적인 것에 의존하면 안된다.
- 자주 변경되는 구체 클래스에 의존하면 안된다.
ACID
- Atomatic
- 원자성
- 트랜잭션의 모든 연산은 반드시 완전하게 수행되거나 안되거나되는 것
- Consistency
- 트랜잭션 작업이 일관된 상태였다면, 트랜잭션 작업이 종료되어도 일관된 상태여야 한다.
- 게시판 제목이 255자였다면, 끝나도 255자여야 한다.
- Isolation
- 트랜잭션 작업중에 다른 트랜잭션에 영향을 줘도, 영향을 받아도 안된다.
- 게시판 두개가 동시에 올라가 충돌이난다면, 한 개의 게시판 트랜잭션이 끝날때까지 다른 트랜잭션은 실행하지 않는다.
- Durability
- 성공적으로 트랜잭션이 되었다면, 그 트랜잭션은 영구저장이된다.
HTTP 버전 별 차이
- HTTP 0.9
- GET
- HTTP HEADER가 없음
- HTML 문서만 전송 가능
- HTTP 1.0
- 상태코드가 응답값 시작부분에 포함이 되어 요청에 대한 실패와성공을 알 수 있음
- MEHTOD : POST HEAD 추가
- HTTP HEADER 추가
- Content-Type 추가로 다양한 파일 전송
- Request 별 새로운 Connection 생성
- kepp-alive (멍청한 프록시/벤더사마다 있는곳도 있고 없는것도 있다.)
- HTTP 1.1
- OPTION, PUT, DELETE 추가
- 지속 커넥션(TCP 커넥션 재활용 -> 3wayhandshake 비용감소)
- 요청한 것에 응답 순서도 중요하기 때문에 -> 데이터 레이턴시(성능하락)
- 요청에 대한 응답이 늦어지면 다음 요청도 늦어짐
- 파이프라인
- HTTP Request 여러개 미리 보낸다.
- 헤더 압축으로 성능 향상
- HTTP 2.0
- MultiPlexing
- 데이터를 병력적으로 받아온다.
- 프레임 단위로 전송 관리가 됨
- 다수의 응답과 요청이 가능
- 요청과 순서에 구애받지 않는다.
- 데이터 구조
- 바이너리로 인코딩하여 전송
- 서버 푸시
- 브라우저에 필요한 리소스들을 서버가 알아서 찾아다가 내려줌
- 필요한 경우
- 캐싱되지 않은 리소스를 받아올 때
- 페이지에서 필요한 리소스가 페이지를 내려주는 서버가 있을 때
- MultiPlexing
- QUIC
- UDP 사용
- 어플리케이션 계층에 신뢰성 구현
- 독립 스트림
트랜잭션 고립수준
- Read Uncommitted
- 어떤 트랜잭션의 변경 내용이 커밋이나 롤백과 상관없이 다른 트랜잭션에서 보여진다.
- A 트랜잭션에서 10번 사원의 나이를 27 > 28로 변경
- uncommit
- B 트랜잭션에서 10번 사원 나이 조회
- 28살 조회 (DIRTY READ)
- A 트랜잭션 문제가 생겨 롤백
- B 트랜잭션은 10번 사원이 여전히 28살이라고 생각하고 로직 수행
- Read Committed
- 어떤 트랜잭션의 변경 내용이 커밋되어야만 다른 트랜잭션에서 조회할 수 있다.
- B 트랜잭션에서 10번 나이를 조회
- 27살 조회
- A 트랜잭션에서 10번 나이를 28살로 바꾸고 커밋
- B 트랜잭션에서 10번 나이 재 조회
- 28살 조회
- Non Repeatable Read 문제( 트랜잭션 내에서 항상 똑같은 결과를 반환해야한다는 정하성에 어긋난다.)
- Repeatable Read
- 트랜잭션이 시작되기 전에 커밋된 내용에 대해서만 조회할 수 있는 격리수준
- NonRepeatable read 부정합이 발생하지 않음
- 10번 트랜잭션이 50번 사원 조회
- 12번 트랜잭션이 50번 사원 이름 변경하고 커밋
- 10번 트랜잭션 50번 사원 재조회
- 언두 영역에서 백업된 데이터 반환
- 자신의 트랜잭션번호보다 낮은 트랜잭션 번호에서 변경된 것만 보게 되는 것
- phantom read
- 한 트랜잭션 내에서 같은 쿼리를 두 번 실행했는대, 첫 번째 쿼리에서 업선 유령 레코드가 두 번째 쿼리에서 나타나는 현상
- Serializable
- 가장 단순하고 엄격한 격리 수준
- 격리 수준이 Serializable일 경우 읽기작업도 공유 잠금을 설정
dfs vs bfs
- dfs
- 루트노드에서 시작해서 다음 분기로 넘어가기 전에 해당 분기를 완벽하게 탐색하는 방법(자식우선)
- 스택 또는 재귀 함수로 구현
- 방문한 노드는 반드시 확인해야한다.
- bfs
- 루트노드부터 시작해서 인접한 노드부터 방문하는 방법
- 큐를 이용
- 최단거리 사용할 때
스레드 세이프
- 여러 스레드로부터 동시에 접근이 이루어져도 프로그램 실행에 문제가없음
- 스레드 세이프 지키기 위한 방법
- 어떤 함수가 한 스레드에 의해 호출되어 실행 중일 때, 다른 스레드가 그 함수를 호출해도 결과는 각각 올바르게 주어져야한다.
- 공유 자원의 사용을 최대한 줄여 동시 접근을 막는다.
- 상호배제(공유자원을 사용할 경우 해당 자원의 접근을 세마포어 등의 락으로 통제한다)
페이징 교체 알고리즘
- FIFO
- 메모리에 올라온 지 가장 오래된 페이지를 교체한다.
- 활발하게 사용 중인 페이지를 계속 교체한다면 페이지 부재율이 높아지고 실행속도가 떨어질 위험이 있다.
- Optimal (최적 페이지 교체)
- 앞으로 가장 오랫동안 사용되지 않을 페이지를 교체
- 프로세스가 앞으로 사용할 페이지를 미리 알아야한다.
- 구현이 어렵다.
- LRU(least recently used)
- 가장 오래 사용되지 않은 페이지를 교체하는 알고리즘
- 페이지가 사용될 시간을 미리 알고 있다.
- 페이지가 사용될 시간을 예측
- MFU(most frequently used)
- 참조 횟수가 가장 많은 페이지를 교체
Junit 생명주기
- @BeforeClass : 테스트 클래스 시작 시 한번만 수행
- @Before : 각 테스트 시작 전 호출, 테스트 값/상태 준비
- @Test : 테스트 케이스, void 반환
- @After : 테스트 케이스 종료 후 호출, 테스트 후 정리
- @AfterClass : 모든 테이스 케이스 완료 후
Functional Interface
- Predicate
- 하나의 인자와 리턴 타입을 가진다. 반환타입이 boolean으로 고정
Predicate<String> p = str -> str.isEmpty(); boolean result = p.test("AAA");
- Consumer
- 리턴 타입이 void, 인자를 받는 메서드, 인자를 소모한다Consumer<String> c = str -> System.out.println(c); c.accept("hi");
- Function
- 함수를 지원해준다. 하나의 인자와 리턴타입을 가진다.
Function<String, Integer> function = str -> Integer.parseInt(str); Integer result = function.apply("1");
- Supplier
- 인자를 받지 않으며 리턴타입만 존재하는 메서드. 순수함수에서 결과를 바꾸는건 오직 input 값 뿐인대, input 값이 없다는건 항상 같은 값만리턴
Supplier<String> s = () -> "Hello Supplier"; String result = s.get();
AOP
- 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 공통된 로직이나 기능을 하나의 단위로 묶는다.
- 각 메서드에서 동일한 일을 하는 코드가 흩어져 사용되는 것을 AOP는 중복되는 코드를 떼어내서 분리하고, 메서드는 자신이 해야할 일만 하면 된다.
- @Aspect
- 여러 곳에서 쓰이는 코드를 모듈화 한 것
- @Target
- Aspect가 적용되는 곳
- @Advice
- Aspect에서 실질적인 기능에 대한 구현체
- @JointPoint
- Advice가 Target에 적용되는 시점
- 메서드 진입시, 생성자 호출 시, 필드에서 값 꺼낼 시
- @PointCut
- Joint Point의 상세스팩
JPA란?
- 객체는 객체대로 설계하고 관계형 데이터베이스는 데이터베이서대로 설계했을 때 ORM 프레임워크가 중간에서 매핑해준다. 이것이 JPA
- 특징
- 1차캐시와 동일성 보장
- 동일한 트랜잭션에서 같은 엔티티에 대한 두 번의 조회를 하나의 SQL문으로 수행해서 캐시함
- 지연로딩과 즉시로딩을 지원해 JOIN으로 연관객체를 같이 조회할 수 있다.
- 생산성
- 1차캐시와 동일성 보장
- Hibernate
- JAP의 구현체, SQL문을 직접 작성하지 않고 메서드 호출만으로 쿼리수행을 가능하게 해서 생산성을 높일 수 있다.
- 영속성 컨텍스트
- 엔티티를 영구 저장하는 환경, 애플리케이션과 데이터 베이스 사이에서 객체를 보관하는 가상의 환경, 생명주기는 트랜잭션과 동일(트랜잭션이 종료되면 영속성도 종료)
- 장점
- 1차 캐시: 똑같은 걸 두번 조회하는 경우, 처음 조회할 때 해당 데이터를 1차 캐시에 올려서 두 번쨰 조회할 때는 쿼리를 수행하지 않고 캐싱된 데이터를 가져온다(생산성)
- 동일성 보장: 1차 캐시에 있는 엔티티는 여러번 조회했을 때 동일성을 보장
- 트랜잭션을 지원하는 쓰기 지연
- 커밋이 되기 전까지는 쓰기 지연 저장소에 모아뒀다가 커밋하는 시점에 한번에 flush한다.
- 변경감지
- 영속성 컨텍스트에 들어가 있는 엔티티에 대해 변경을 한다고 했을 때, 1차 캐시에 처음 저장할 때 동시에 스냅샷 필드를 저장한다. 그래서 Commit이나 flush가 일어날 때 엔티티의 현재 값과 스냅샷을 비교하여 변경사항이 있으면 알아서 update 쿼리가 수행한다.
- 지연로딩
- 프록시: 실제 엔티티 대신에 사용되는 객체로 원본 엔티티를 상속받은 객체. 초기에 db로부터 조회가 되면 프록시 객체의 타겟으로 실제 객체가 연결되면서 최초에 1회 초기화가 된다. 그 이후로는 지속적으로 호출해도 프록시가 초기화 되지 않고 한번 호출한 걸 계속 쓴다.
- Member안에 Team 객체가 있을 때 Team 멤버를 조회하는게 아니라면 굳이 실제로 데이터베이스에서 조회해서 1차 캐시에 올려놓을 필요가 없다. 이때 가짜 객체를 조회. 이미 1차캐시에 이전에 조회된 데이터가 있다면 그 실제 데이터를 가져온다.
- 동작 순서
- JPQL 호출 -> 데이터베이스 우선 조회
- 조회된 값을 영속성 컨텍스트에 저장
- 영속성 컨텍스트에 조회할 때 이미 조회한 게 있다면 데이터를 버린다.
- N+1문제
- 하나의 쿼리를 수행하는데 N개의 쿼리가 더 수행 된다는 의미
- 멤버 객체와 팀 객체가 연관되어있다. 10명의 멤버를 조회할 때, 지연 로딩으로 가져오면 팀은 프록시 객체를 사용하고 직접 디비를 조회하지 않는다. 근데 즉시로딩을 사용 시 값을 가져올 때 실제 데이터로 채워져있어야 해서 팀까지 디비에서 조회해야 한다. 이때 10명의 멤버를 조회하기 위해 select 쿼리를 사용하면 멤버에 대해 각각 select team의 쿼리를(10개)가 나가 매우 비효율적인 상황이 된다.
- JQPL은 JPA에서 제공하는 메서드와 달리 영속성 컨텍스트 조회가 아니라 디비에 바로 접근한다.
- 해결방법
- batch size
- @BatchSize 어노테이션을 활용하면 설정한 size 만큼 데이터를 미리 로딩한다. 즉 연관 엔티티를 조회할 때 사이즈만큼 where in 쿼리를 통해 조회하고, size를 넘어갔을 때 추가로 where in 쿼리수행, 결국 정해징 batch size에 따라 반복적인 쿼리가 수행되니 추천하지 않음
- 패치조인
- @Query(“select m from member m left join fetch m.orders”)와 같이 적으면 fetch를 사용해서 조인쿼리를 수행. fetch 키워드는 연관 객체나 컬렉션을 한 번에 같이 조회하게 된다. 페치 조인을 사용하면 연관 앤티티는 프록시가 아닌 실제 엔티리를 조회하게 되고 이로써 연관 객체까지 한번의 쿼리로 다 가져올 수 있다. N번 실행하지 않게 된다.
- batch size