☞ 목차 펴기
◈ C와 Java의 차이점
▷ C 언어
- 인간의 명령어를 컴퓨터가 수용할 수 있는 기계적 언어로 변환시켜주는 역할을 한다. 시스템 간 호환성, 이식성이 좋고 고급언어와 저급언어 간의 인터페이스가 용이하다는 특징이 있다. 또한 절차지향 프로그래밍의 대표적 언어로 컴퓨터의 작업 처리 방식과 유사하기 때문에 객체지향 언어에 비해 더 빠른 처리 속도를 가지므로 시간적으로 유리하다.
- 절차지향(Process oriented) : 실세계의 상황에서, 업무와 흐름을 위주로 사고하는 방식
▷ Java
- 인터넷의 분산환경에서 사용되도록 설계된 프로그래밍 언어이며, 인터넷 환경 기반의 프로그램을 만들고 수행 시킬 수 있는 응용 프로그램을 만들 수 있다. 객체지향 프로그래밍의 대표적 언어이다.
◈ 자바의 특징 설명
▷ 객체지향 프로그래밍 언어이다.
- 객체지향이란 실세계의 상황을 인식할 때, 객체와 객체들의 관계 위주로 사고하는 방식. 모든 데이터를 객체로 취급하여 프로그램에 반영한 것이며 순차적으로 프로그램이 동작하는 기존의 것들과 달리 객체와 객체의 상호작용을 통해 프로그램이 동작하는 것을 말한다. 코드 재사용 가능, 오류 발생 가능성 낮아짐, 안정성 올라감의 특징을 갖는다.
▷ 기본 자료형을 제외한 모든 요소들이 객체로 표현되고, 객체 지향의 특징인 자료 추상화, 상속, 다형성, 캡슐화, 동적 바인딩이 적용된 언어이다.
- 자료추상화(Data abstraction) : 사물을 표현할 때, 그 사물과 밀접한 연관이 있는 데이터와 함수를 묶어서 표현하는 방식
- 상속(Inheritance) : 기존에 구현된 클래스로부터 자원(Data, 함수)들을 물려받아 재사용하는 방식
- 다형성(Polymorphism) : 사물들의 같으면서도 다르고, 다르면서도 같은 면을 표현하는 방식 → 하나의 객체가 여러가지 타입을 가질 수 있는 것을 의미함
- 캡슐화(Encapsulation) : 어떤 클래스를 사용함에 있어 그 클래스가 제공하는 메서드의 기능만을 알고 사용할 뿐, 실제로 그 메서드가 어떻게 움직이는지 굳이 알 필요가 없다는 것을 의미함.
- 동적 바인딩(Dynamic binding) : Binding(함수 호출과 함수 정의를 연결하는) 작업이 실행시에 이루어지는 방식
▷ 그 외 특징
- ① JVM(자바가상머신) 위에서 동작함으로 운영체제에 독립적이다. ② 보안성이 뛰어나다 ③ Garbage Collector가 자동적으로 메모리를 관리해준다. ④ 멀티 쓰레드를 지원한다. ⑤ 분산 환경에 적합하여 네트워크 수행 능력이 뛰어나다.
- JVM 위에서 동작하기 때문에 실행 속도가 상대적으로 느리다.
- 다중 상속이나 타입에 엄격하며, 제약이 많다.
+) 상속
- 부모클래스의 속성과 메서드를 자식클래스가 이어받는 것
- 코드의 재사용성을 증대시킬 수 있으며 폭 넓게 사용하는것이 가능해진다.
- 상속에는 extends의 방법과 has~a의 방법이 있는데, extends는 모든 메서드를 오버라이드 해야 하고, has~a는 필요한 메서드만 가지고 올 수 있다.
+) 다형성
- 하나의 클래스나 메서드가 다양한 방식으로 동작이 가능한 것을 의미함.
1. 서로 상속 관계에 있는 클래스 사이에서만 타입 변환을 할 수 있다.
2. 자식 클래스 타입에서 부모 클래스 타입으로의 타입 변환은 생략할 수 있다.
3. 하지만 부모 클래스 타입에서 자식 클래스 타입으로의 타입 변환은 반드시 명시해야 한다.
> instanceof 연산자 : 다형성으로 인해 런타임에 참조 변수가 실제로 참조하고 있는 인스턴스의 타입을 확인할 수 있도록 해준다.
▶ Overriding(오버라이딩) : 상위 클래스에 있는 메서드를 하위 클래스에서 재정의 하는 것
▶ Overloading(오버로딩) : 매개변수의 개수나 타입을 다르게 하여 같은 이름의 메서드를 여러개 정의하는 것
+) 캡슐화
> 데이터 은닉
- 캡슐화의 특성으로 클래스의 속성들을 private으로 만들어 클래스 밖에서 함부로 건드리지 못하게 하는 것을 말한다. 클래스를 사용함에 있어서 속성들에게 직접 접근하지 못하게 하고 getter, setter 메서드들을 통해 접근하게 한다.
◈ JVM의 역할 및 구조
- JVM = Java virtual machine, 가상 머신 프로세스로서 자바 bytecode를 실행.
- JVM은 스택 기반으로 동작하며, Java Byte Code를 OS에 맞게 해석해주는 역할을 하고 Garbage Collector를 통해 자동적인 메모리 관리를 해준다.
- Java compile 과정 :
① 프로그램 실행시 OS로부터 프로그램이 필요로 하는 메모리를 먼저 할당 받는다. ② 자바 컴파일러(javac 명령어)를 통해 개발자가 작성한 코드 (.java)를 바이트 코드 (.class)로 변환한다. ③ Class Loader에서 바이트 코드를 JVM에 로딩 시킨다. ④ 로딩된 바이트 코드를 execution engine을 통해 기계어로 해석한다. ⑤ 해석된 바이트 코드들은 runtime data areas에 배치되어 실질적인 수행이 이루어진다.
◈ JDK와 JRE
- JDK = Java Development Kit, 사용자가 Java 응용 프로그램을 개발, 컴파일 및 실행할 수 있도록 JRE, 컴파일러 및 도구를 포함한 키트를 말함
- JRE = Java Runtime Environment, 기본적으로 Java 프로그램이 실행되는 JVM
◈ Call by Reference와 Call by Value의 차이
- Call by Reference : 매개변수의 원래 주소에 값을 저장하는 방식 (클래스 객체를 인수로 전달한 경우 등)
- Call by Value : 인수로 기본 데이터 타입을 사용. 주어진 값을 복사하여 처리하는 방식. 메서드 내의 처리 결과는 메서드 밖의 변수에 영향을 미치지 않음.
◈ Primitive type과 Reference type
- Primitive type : 변수에 값 자체를 저장(int, double, char, boolean 등). Wrapper class를 통해 객체로 변환 가능
- Reference type : 메모리상에 객체가 있는 위치를 저장(Class, Interface, Array 등)
◈ Garbage Collection 이란?
- JVM의 메모리 관리 기법 중 하나로 시스템에서 동적으로 할당됐던 메모리 영역 중에서 필요없어진 메모리 영역을 회수하여 메모리를 관리하는 기법
- Garbage Collection의 작업을 수행하기 위해 JVM이 어플리케이션의 실행을 잠시 멈추고, Garbage Collection을 실행하는 쓰레드를 제외한 모든 쓰레드들의 작업을 중단 후 (Stop The World 과정이라 한다고 함) 사용하지 않는 메모리를 제거(Mark and Sweep 과정)하고 작업이 재개된다.
- Garbage Collection 작업은 Young 영역에 대한 Minor Garbage Collection과 Old 영역에 대한 Major Garbage Collection으로 구분된다.
◈ 추상 클래스와 인터페이스 특징 및 차이
▷ 추상 클래스(Abstract class)
- 클래스 내 추상 메서드가 하나 이상 포함되거나 abstract로 정의된 경우를 말함
- 추상 메서드 1개 이상, 생성자, 일반 변수, 일반 메서드 선언 가능
- 메서드의 부분 구현이 가능 (부분 구현된 메서드를 상속받아 확장시키기 위함)
▷인터페이스(Interface)
- 모든 메서드가 추상 메서드로만 이루어져 있는 것을 말함
- 추상 메서드, 상수만 선언 가능
- 생성자, 일반 변수를 가질 수 없음
- 팀별 협업 시 추상 메서드를 통해 결과를 예측하고 작업이 가능함 (구현 객체의 동일성 보장)
공통점 → ① new 연산자로 인스턴스 생성 불가능. ② 사용하기 위해서는 하위 클래스에서 확장/구현 해야 함
차이점 → ① 인터페이스는 해당 인터페이스를 구현하는 모든 클래스에 대해 특정한 메서드가 반드시 존재하도록 강제한다. ② 추상 클래스는 상속받는 클래스들의 공통적인 메서드들을 추상화시키고, 기능 확장을 위해 사용한다. ③ 추상 클래스는 다중 상속이 불가능하지만, 인터페이스는 다중 상속이 가능하다.
◈ 불변 객체(Immutable Object)란?
- 객체 생성 후 내부의 상태를 바꿀 수 없는 객체를 말한다.
- 재할당은 가능하지만, 한번 할당하면 내부 데이터를 변경할 수 없는 객체
- Java에서는 필드가 원시 타입인 경우 final 키워드를 사용해 불변 객체를 만들 수 있다.
- 참조 타입인 경우에는 추가적인 작업이 필요함
↳ ① 객체를 참조하는 경우 : 참조 변수가 일반 객체인 경우 객체를 사용하는 필드의 참조 변수도 불변 객체로 변경해야 한다.
↳ ② 배열을 참조하는 경우 : 배열을 받아 copy해서 저장하고, getter를 clone으로 반환하도록 하면 된다. (배열을 그대로 참조하거나 반환할 경우 외부에서 내부 값을 변경할 수 있기 때문에 clone을 반환해 값 변경 위험을 막는다.)
↳ ③ 리스트인 경우 : 배열과 마찬가지로 생성시 새로운 List를 만들어 값을 복사하도록 한다. (자료구조 내부를 복사하여 전달하는 것을 defensive-copy라고 한다.)
- String, Integer, Boolean 등도 불변 객체이다. → String s = "a", s = "b" 이런 식으로 사용하기 때문에 값이 변경된다고 생각하여 불변객체가 아닌 것으로 착각하기 쉬우나 str이 처음에 참조하고 있는 "a"를 "b"로 변경하는 것이 아닌 새로운 객체를 만들고 그 객체를 str이 참조하게 하는 것이다.
▷ 불변 객체 사용 이유
- Thread-Safe 하므로 병렬 프로그래밍에 유용하며, 멀티 쓰레드 환경에서 동기화를 고려하지 않고 객체 공유 가능. (공유 자원이 불변이기 때문에 항상 동일한 값을 반환하기 때문이다.)
- 실패 원자적인 메서드를 만들 수 있다. 즉, 어떠한 예외가 발생되더라도 메서드 호출 전의 상태를 유지할 수 있어 예외 발생 전과 똑같은 상태로 다음 로직을 처리할 수 있다.)
- 부수 효과(변수값 변경, 객체 필드값 설정 등의 예외나 오류로 실행이 중단되는 현상)를 피해 오류를 최소화 할 수 있다.
- 메서드 호출 시 파라미터 값이 변하지 않는다는 것을 보장할 수 있다.
- 가비지 컬렉션 성능을 높일 수 있다. (Garbage Collector가 스캔하는 객체의 수가 줄기 때문에 Garbage Collection 수행시 지연시간도 줄어든다.)
- 객체에 대한 신뢰도가 높아진다. (transaction 내에서 그 객체가 변하지 않기에 믿고 쓸 수 있음)
- 생성자, 접근 메서드에 대한 방어 복사가 필요 없음.
+ 단점으로는 객체가 가지는 값마다 새로운 객체가 필요하므로 메모리 누수나 성능 저하를 일으킬 수 있음.
◈ String과 StringBuffer, StringBuilder의 차이
▷ String
- Immutable(불변)
- 객체 할당 시 메모리 공간에 변동 없음. (Heap String pool 영역에 생성되어 그 값을 계속 사용)
- 동기화 신경쓰지 않아도 됨
- 엄청난 양의 문자열을 선언 및 연산할 시 성능저하를 고려해야 함.
> String이 불변 객체인 이유
- 캐싱 기능에 의한 메모리 절약과 속도 향상 (Java에서 String 객체들은 Heap의 String pool이라는 공간에 저장되는데, 참조하려는 문자열이 String pool에 존재하는 경우 새로 생성하지 않고 pool에 있는 객체를 사용하기 때문에 특정 문자열 값을 재사용하는 빈도가 높을수록 상당한 성능 향상을 기대할 수 있다.)
- Thread-safe (String 객체는 불변이기 때문에 여러 쓰레드에서 동시에 특정 String 객체를 참조하더라도 안전하다)
- 보안 기능 (중요한 데이터를 문자열로 다루는 경우 강제로 해당 참조에 대한 문자열 값을 바꾸는 것이 불가능하기 때문에 보안에 유리함)
▷ StringBuffer
- Mutable(가변)
- 각 메서드 별로 Synchronized keyword가 존재함.
- 동기화를 지원하므로 멀티쓰레드 환경에서 많이 쓰임. (Thread-safe)
▷ StringBuilder
- Mutable(가변)
- 동기화 지원하지 않아 싱글쓰레드 환경에서 주로 사용.
◈ new String()과 ""의 차이
- Java의 문자열은 Heap 영역 내의 String pool에서 따로 관리함.
- ""으로 선언된 String은 Heap 안에 있는 String constant pool에 추가되고 해당 값을 참조 값으로 가지게 됨.
- new String()으로 생성된 String은 new 키워드로 새로운 객체를 생성하기 때문에 String pool이 아닌 Heap 영역에 저장하게 됨.
- 메모리 상의 위치가 다른 것.
◈ Java의 메모리 영역
▷ 메서드 영역
- static 변수, 전역 변수, 코드에서 사용되는 Class 정보 등이 할당됨
▷ 스택(Stack) 영역
- 지역 변수, 함수(메서드) 등이 할당되는 LIFO 방식의 메모리
▷ 힙(Heap) 영역
- new 연산자를 통한 동적 할당된 객체들이 저장되며, 가비지 컬렉션에 의해 메모리가 관리됨
◈ 클래스 멤버 변수 초기화 순서
① static 변수 선언부 : 클래스가 로드될 때 변수가 제일 먼저 초기화 됨
② 필드 변수 선언부 : 객체가 생성될 때 생성자 block보다 앞서 초기화 됨
③ 생성자 block : 객체가 생성될 때 JVM이 내부적으로 locking (Thread-safe 영역)
◈ static이란?
- static keyword를 사용한 변수나 메서드는 클래스가 메모리에 올라갈 때 자동으로 생성되며 클래스 로딩이 끝나면 바로 사용할 수 있음. 즉, 인스턴스 생성 없이 바로 사용 가능
- 모든 객체가 메모리를 공유한다는 특징이 있고, Garbage Collector 관리 영역 밖에 있기 때문에 프로그램이 종료될 때까지 메모리에 값이 유지된 채로 존재하게 됨
> 사용이유
- 자주 변하지 않는 값이나 공통으로 사용되는 값 같은 공용자원에 대한 접근에 있어서 매번 메모리에 로딩하거나 값을 읽어들이는 것보다 일종의 '전역변수'와 같은 개념을 통해 접근하는 것이 비용도 줄이고 효율을 높일 수 있음.
- 인스턴스 생성없이 바로 사용 가능하기 때문에 프로그램 내에서 공통으로 사용되는 데이터들을 관리할 때 이용함.
- non static 변수를 static context에서 접근할 수 있는가? → 자바에서 정적 변수는 해당 클래스에 속하며 해당 값은 모든 인스턴스에서 동일하게 유지됨. 코드가 인스턴스 없이 정적인 아닌 변수에 접근하려고 하면 해당 변수가 아직 생성되지 않았으며 인스턴스와 연결되지 않았기 때문에 컴파일 에러가 남.
◈ 쓰레드(Thread)의 정의와 특징
- 쓰레드란 프로세스 내에서 동시에 실행되는 독립적인 실행 단위를 말함.
- 어플리케이션의 성능을 향상시킬 수 있는 특징을 가짐.
- 쓰레드는 각각의 스택 메모리 영역을 가지고 있음
- 프로세스와 달리 쓰레드는 동일한 프로세스 내의 다른 쓰레드들과 전역 메모리를 공유하며, 이에 반해 프로세스는 자기 자신의 메모리 영역을 가짐
▷쓰레드의 장점
- 빠른 프로세스 생성
- 적은 메모리 사용
- 쉬운 정보 공유
▷쓰레드의 단점
- 교착상태에 빠질 수 있음 (다중 프로그래밍 체제에서 하나 혹은 그 이상의 프로세스가 수행할 수 없는 어떤 특정시간을 기다리고 있는 상태, 무한정 대기 상태)
▷프로세스와 쓰레드의 차이
- 프로세스(process)
↳ 프로세스란 단순히 실행 중인 프로그램(program)이라고 할 수 있음
↳ 즉, 사용자가 작성한 프로그램이 운영체제에 의해 메모리 공간을 할당받아 실행중인 것을 말함
↳ 이러한 프로세스는 프로그램에 사용되는 데이터와 메모리 등의 자원, 쓰레드로 구성됨
- 쓰레드(thread)
↳ 쓰레드란 프로세스 내에서 실제로 작업을 수행하는 주체를 말함
↳ 모든 프로세스에는 한 개 이상의 쓰레드가 존재하여 작업을 수행함
↳ 두 개 이상의 쓰레드를 가지는 프로세스를 멀티쓰레드 프로세스(multi-threaded process)라고 함.
▷ 쓰레드의 생성과 실행
- 자바에서 쓰레드를 생성하는 방법
① Runnable 인터페이스를 구현한다.
② Thread 클래스를 상속받는다.
↳ 두 방법 모두 쓰레드를 통해 작업하고 싶은 내용을 run() 메서드에 작성하면 된다.
> wait와 sleep 메서드의 차이
▷ wait
- 현재 쓰레드는 객체에 대한 호출이 있을 때 lock 객체에서 동기화 됨
- 동기화는 여러 쓰레드에서 동일한 객체에 엑세스하는데 사용됨.
- 다른 물체가 잠금을 풀면 잠금이 유지됨
- 객체에서 notify(), notifyAll()을 호출하면 wake up함
- 시간 동기화
▷ sleep
- 쓰레드 호출은 현재 실행중인 쓰레드에서 발생함.
- 동기화는 여러 쓰레드에서 Sleeping 쓰레드를 위해 사용됨.
- 시간초과가 지정되거나 누군가 중단된 경우 최소 t회 동안 잠금 상태를 유지함.
- 적어도 시간이 만료되거나 interrupt() 호출 시까지 sleep
- 멀티 쓰레드 동기화
> 쓰레드가 교착상태 없이 자원에 접근할 수 있도록 하려면?
- 쓰레드를 사용하는 동안 교착 상태를 피하는 가장 간단한 방법은 lock에 순서를 부여하고 각 쓰레드가 해당 순서를 따르도록 하는 것. 만일 모든 쓰레드가 뮤텍스를 lock, unlock을 동일한 순서로 진행하면 교착상태는 발생하지 않음.
◈ Synchronized(동기화)란?
- 여러 개의 쓰레드가 하나의 자원에 접근하려 할 때, 현재 데이터를 사용하고 있는 쓰레드를 제외하고 나머지 쓰레드들은 데이터에 접근할 수 없게 막는 것.
- 데이터의 thread-safe를 하기 위해 자바에서 Synchronized 키워드를 제공해 멀티 쓰레드 환경에서 쓰레드간 동기화를 시켜 데이터의 thread-safe를 보장한다.
- Synchronized는 변수와 메서드에 사용해서 동기화 할 수 있으며, Synchronized 키워드를 남발하게 되면 오히려 프로그램의 성능 저하를 일으킬 수 있음.
- 둘 이상의 쓰레드가 공동의 자원(파일이나 메모리 블록)을 공유하는 경우, 순서를 잘 맞추어 다른 쓰레드가 자원을 사용하고 있는 동안 한 쓰레드가 절대 자원을 변경할 수 없도록 해야 함.
- 한 쓰레드가 파일에서 레코드를 수정하는데, 다른 쓰레드가 동시에 같은 레코드를 수정하면 심각한 문제가 발생할 수 있음. → 이런 상황을 막기 위해 쓰레드에 대한 동기화를 하는 것
- 공유 자원이 사용되어 동기화가 필요한 부분을 임계 영역(critical section)이라고 부르며, Java에서는 임계영역에 synchronized 키워드를 사용함.
- synchronized로 지정된 임계영역은 한 쓰레드가 이 영역에 접근하여 사용할 때 lock이 걸림으로써 다른 쓰레드가 접근할 수 없게 된다. 이후 해당 쓰레드가 이 임계영역의 코드를 다 실행 후 벗어나게 되면 unlock 상태가 되어 그때서야 대기하고 있던 다른 쓰레드가 이 임계영역에 접근하여 다시 lock을 걸고 사용할 수 있게 된다.
- lock은 해당 객체당 하나씩 존재하며, synchronized로 설정된 임계영역은 lock 권한을 얻은 하나의 객체만이 독점적으로 사용함.
- 한 쓰레드가 만든 변화를 다른 쓰레드에서 확인할 수 있게 해준다. (쓰레드간 통신, 가시성)
▷ 동기화 하기 위한 방법
- synchronized 함수(메서드)를 만들어 사용
↳ 메서드 이름 앞에 synchronized 키워드를 사용하면 해당 메서드 전체를 임계영역으로 설정할 수 있음
synchronized void increase() {
count++;
System.out.println(count);
}
- synchronized 블록(block)을 사용
↳ 동기화를 많이 사용하게 되면 효율이 떨어질 수 있으므로 꼭 필요한 부분에만 블럭을 지정하여 임계영역으로 설정할 수 있다. 다음과 같이 synchronized(this)로 지정한다면 참조변수(this) 객체의 lock을 사용하게 된다.
void increase() {
synchronized(this) {
count++;
}
System.out.println(count);
}
> 메서드와 동기화된 block의 차이점
- 자바에서 각 객체들은 lock을 가지고 있음. 쓰레드는 동기화된 키워드를 사용하여 객체에 대한 lock을 획득할 수 있음.
- 동기화된 키워드는 메서드 레벨 또는 코드의 블록레벨에 적용할 수 있음.
> 모니터 내부에서 쓰레드 동기화는 어떻게 이루어 지는가?
- JVM은 모니터와 함께 lock을 사용함. 모니터는 기본적으로 동기화된 코드 시퀀스를 감시하고 한번에 하나의 쓰레드만 동기화된 코드를 실행하도록 함. 각 모니터는 객체 참조와 연관됨. 쓰레드는 lock을 얻을 때 까지 코드를 실행할 수 없음
◈ volatile
- 멀티 쓰레드 프로그래밍에 사용되는 키워드
- synchronized 키워드는 모든 코어가 해당 메모리를 일관되게 바라보는 지연시간이 멀티 코어 환경에서 굉장히 낭비되는 단점이 있다. 또한 가상 머신의 최적화도 방해한다.
- 쓰레드 간의 통신 만이 목적이라면 volatile 키워드를 고려해 볼 수 있다. volatile 키워드를 변수 앞에 붙이면 코어마다 있는 캐시가 아닌 메모리에서 직접 읽고 쓴다. volatile 키워드는 항상 가장 최근에 기록된 값을 읽게 됨을 보장한다. 또한 volatile은 lock-free하기 때문에 성능면에서도 좋음.
- 다른 쓰레드들에 의해 변수의 값이 수정될 수 있음. 변수에 액세스 할 때마다 동기화가 수행됨
◈ transient
- 해당 변수가 속한 클래스가 직렬화 되더라도 transient 변수는 직렬화 되지 않음.
- 민감한 정보를 직렬화 하지 않도록 제외하고 싶은 경우 사용
- 필드의 값이나 객체가 계산해서 얻을 수 있는 값인 경우 사용
/* ◈ Atomic class
- java.util.concurrent.atomic 패키지에 lock-free 하면서 thread-safe 한 기능을 지원하는 클래스들이 있다. AtomicLong, AtomicInteger 등이 해당 패키지에 있는 클래스들이다. */
◈ 싱글톤(Singleton) 클래스란?
- 싱글톤 패턴은 애플리케이션이 시작될 때 어떤 클래스가 최초 한번만 메모리를 할당하고 그 메모리에 인스턴스를 만들어 사용하는 디자인 패턴
- 싱글톤 클래스를 위해서는 먼저 클래스의 객체, 인스턴스가 하나만 존재하는지 확인하고 해당 인스턴스에 대한 글로벌 액세스를 제공. 클래스의 모든 생성자를 private으로 선언해야 하며, 인스턴스에 대한 참조를 반환하는 정적 메서드를 제공
◈ try-with-resources 란?
- try-catch-finally의 문제점을 보완하기 위해 나온 개념
- try 문 안에 자원 객체를 전달하면, try 블록이 끝나고 자동으로 자원 해제를 해주는 기능이다.
- 따로 finally 구문이나 모든 catch 구문에 종료 처리를 하지 않아도 되는 장점이 있다.
- 코드의 더 나은 가독성을 위해 사용
- try-with-resources 사용을 위해서는 close 메서드를 정의하기 위한 AutoCloseable 인터페이스를 구현해야 한다.
◈ final / finally / finalize() 차이
▷ final
- 클래스, 메서드, 변수, 인자를 선언할 때 사용할 수 있으며, 한 번만 할당하고 싶을 때 사용한다.
- 한번 초기화되면 그 이후에 변경 불가 (final variable)
- 다른 클래스가 이 클래스를 상속할 때 메서드 오버라이딩을 금지함 (final method)
- 다른 클래스에서 이 클래스를 상속할 수 없음 (final class)
▷ finally
- try-catch와 함께 사용되며, try-catch가 종료될 때 finally 블록이 항상 수행되기 때문에 마무리 해줘야 하는 작업이 존재하는 경우에 해당하는 코드를 작성해주는 코드 블록이다.
▷ finalize()
- Object 클래스에 정의되어 있는 메서드이며, Garbage Collector에 의해 호출되는 메서드로 절대 호출해서는 안되는 메서드이다. Garbage Collector가 발생하는 시점이 불분명하기 때문에 해당 메서드가 실행된다는 보장이 없고, finalize() 메서드가 오버라이딩 되어 있으면 Garbage Collector가 이루어질 때 바로 작업이 이루어지지 않는다. Garbage Collection이 지연되면서 'Out of Memory Exception'이 발생할 수 있기 때문에 finalize() 메서드를 오버라이딩하여 구현하는 것을 권장하지 않고 있다.
◈ Error와 Exception의 차이
- Error : 실행 중 일어날 수 있는 치명적 오류를 말함. 컴파일 시점에 체크할 수 없고, 오류가 발생하면 프로그램은 비정상 종료되며 예측 불가능한 UncheckedException에 속한다.
- Exception : Error보다 비교적 경미한 오류이며, try-catch를 이용해 프로그램의 비정상 종료를 막을 수 있다.
> 예외처리 방법
① 예외가 발생한 메서드 내에서 직접 처리 (try-catch-finally)
② 예외가 발생한 메서드를 호출한 곳으로 예외 객체를 넘기는 방법 (throws)
③ 사용자 정의 예외 생성 (throws) - 직접 예외 클래스를 작성해서 발생 (①이나 ② 사용)
◈ CheckedException과 UnCheckedException 차이
▷ CheckedException
- 실행하기 전에 예측 가능한 예외를 말함. (확인 시점 = 컴파일 단계)
- 반드시 예외 처리를 해야 함.
- Rollback 하지않음
- IOException, ClassNotFoundException 등이 대표적
- Runtime Exception을 제외한 모든 예외
▷ UnCheckedException
- 실행하고 난 후에 알 수 있는 예외를 말함
- 따로 예외 처리를 하지 않아도 됨. (예외 처리 강제하지 않음)
- Rollback 함
- NullPointerException, ArrayIndexOutOfBoundException 등이 대표적
- Runtime Exception 하위 예외
◈ Serialization(직렬화)란?
- 시스템 내부에서 사용되는 객체 또는 데이터를 외부의 시스템에서도 사용할 수 있도록 바이트(byte) 형태로 데이터를 변환하는 기술이며, 반대로 직렬화된 바이트 형태의 데이터를 다시 객체로 변환하는 과정을 'Deserialization(역직렬화)'라고 한다.
- JVM의 메모리에 상주하고 있는 객체 데이터를 바이트 형태로 변환하는 기술
- 자바에서 입출력을 할 때는 Stream이라는 통로를 통해 데이터가 이동한다. 하지만 객체는 바이트 형이 아니라서 스트림을 통해 파일에 저장하거나 네트워크로 전송할 수 없다. 따라서 객체를 스트림을 통해 입출력하려면 바이트 배열로 변환하는 것이 필요한데, 이것이 직렬화이다. Stream을 통해 받은 직렬화된 객체를 원래 모양으로 만드는 과정을 역직렬화라고 한다.
▷ SerialVersionUID를 선언해야 하는 이유?
- JVM은 직렬화와 역직렬화를 하는 시점의 클래스에 대한 버전 번호를 부여하는데, 만약 그 시점에 클래스의 정의가 바뀌어 있다면 새로운 버전 번호를 할당하게 된다. 그래서 직렬화할때의 버전 번호와 역직렬화를 할 때의 버전 번호가 다르면 역직렬화가 불가능하게 될 수 있기 때문에 이런 문제를 해결하기위해 SerialVersionUID를 사용한다.
- 만약 직렬화할 때 사용한 SerialVersionUID의 값과 역직렬화 하기 위해 사용했던 SVUID가 다르다면 InvalidClassException이 발생할 수 있다.
◈ 박싱(Boxing)과 언박싱(UnBoxing)
▷ Wrapper class : 값을 포장하여 객체로 만드는 것
- 값을 더하는 등 변환시켜야 하는 경우에 포장을 뜯을 필요가 있다. (래퍼 클래스 언박싱 한 뒤에 값 변경, 그리고 다시 박싱의 과정을 거침)
- 래퍼 클래스는 산술 연산을 위해 정의된 클래스가 아니며 생성된 인스턴스의 값만을 참조할 수 있기 때문에 래퍼 클래스 인스턴스에 저장된 값을 직접 변경할 수 없다.
▷ Boxing : 기본 타입의 데이터(Primitive data type)를 래퍼 클래스의 인스턴스로 변환하는 것
▷ UnBoxing : 래퍼 클래스의 인스턴스에 저장된 값을 기본 타입의 데이터로 변환하는 것
◈ Generic(제네릭)이란?
- 데이터의 타입을 하나로 지정하지 않고 사용할 때마다 범용적이고 포괄적으로 지정한다는 의미
- 제네릭 타입을 사용함으로써 잘못된 타입이 사용될 수 있는 문제를 컴파일 과정에서 제거할 수 있어 에러를 사전에 방지할 수 있음. (실행 시 타입 에러가 나는 것보다는 컴파일 시에 에러를 사전에 방지하는 것이 좋다.)
- 제네릭 코드를 사용하면 타입을 국한하기 때문에 요소를 찾아올 때 타입변환을 할 필요가 없어 프로그램 성능이 향상됨.
ArrayList list1 = new ArrayList(); // Generic을 사용하지 않을 경우
list1.add("SuldenLion");
String s = (String)list1.get(0); // 타입 변환이 필요함
ArrayList<String> list2 = new ArrayList<String>(); // Generic을 사용할 경우
list2.add("SuldenLion");
s = list2.get(0); // 타입 변환 필요없음
> Generic 특징
- static 멤버에 타입 변수 T를 사용할 수 없음
↳ T는 인스턴스 변수로 간주되기 때문이며 static 멤버는 인스턴스 변수를 참조할 수 없음
- 제네릭 타입의 배열을 생성하는 것도 허용되지 않음
↳ 제네릭 타입의 참조변수를 선언하는 것은 가능하지만, new T[10]과 같이 배열을 생성하는 것은 new 연산자로 인해 안된다. new 연산자는 컴파일 시점에 타입 T가 무엇인지 명확히 알아야 하기 때문이다.
↳ 제네릭 타입의 배열을 생성할 필요가 있을 때는 new 연산자 대신 "Reflection API"의 new Instance()와 같이 동적으로 객체를 생성하는 메서드로 생성하거나 Object 배열을 생성해서 형 변환하는 방법 등을 사용한다.
> 자주 사용하는 타입 인자
<T> | Type |
<E> | Element |
<K> | Key |
<N> | Number |
<V> | Value |
<R> | Result |
> 한정적 타입 매개변수 (Bounded Type)
- 타입 파라미터들은 바운드(bound) 될 수 있다. → 메서드가 받을 수 있는 타입을 제한할 수 있다는 뜻
↳ 어떤 타입과 그 타입의 모든 서브 클래스들을 허용하거나, 어떤 타입과 그 타입의 모든 부모클래스들을 허용하도록 메서드를 작성할 수 있다.
> 제네릭 와일드 카드
- 코드에서 '?' 를 일반적으로 와일드 카드라고 부른다.
- <?> = 타입 파라미터를 대치하는 것으로 모든 클래스나 인터페이스 타입이 올 수 있다.
- <? extends 상위 타입> = 객체의 하위 클래스만 올 수 있다.
- <? super 하위 타입> = 객체의 상위 클래스만 올 수 있다.
◈ Composition(조합)이란?
- 기존 클래스가 새로운 클래스의 구성요소로 쓰임. 새로운 클래스를 만들고 private 필드로 기존 클래스의 인스턴스를 참조함. (기존 클래스를 상속하는 대신 상속을 받으려고 했던 클래스에서 기존 객체를 참조하는 private 필드로써 두는 것)
- 상속을 사용하는 경우에는 상속하는 클래스가 서로 다른 패키지에 존재하여 관리를 별도로 할 경우에 의도치 않은 오류가 발생할 수 있으며, 확장을 목적으로 설계하지 않은 일반적인 클래스를 상속받으면 위험할 수 있음. (상위 클래스의 구현이 하위 클래스에게 노출되는 상속은 캡슐화를 깨뜨림). 이를 해결하기 위한 방법이 Composition.
- Composition을 사용하면 메서드를 호출하는 방식으로 동작하기 때문에 캡슐화를 깨뜨리지 않으며, 기존 클래스의 변화에 영향이 적어지며 안전함.
- 자기 자신에 대한 참조를 다른 객체에 넘겨, 나중에 필요할 때 역호출하도록 요청하는 callback 프레임워크와 사용하기에는 적합하지 않다고 함.
- 강력하게 연관된 Aggregation이며, 자식 오브젝트는 자신의 라이프 사이클을 가지지 않고 부모 객체가 소멸될경우 함께 소멸됨. (집이 없어지면 딸려있는 방도 함께 없어짐)
> 재정리(추가 정리)
+ 상속으로 구현하게 되면 상위 클래스의 모든 public 메서드가 클라이언트에게 공개됨. 하지만 Composition을 사용해 구현하게 되면 개발자가 원하는 메서드만 클라이언트에게 공개할 수 있음.
+ 상위 클래스의 내부 구현을 숨길 수 있음.
+ Java에서 지원하지 않는 다중 상속의 목적을 달성할 수 있음
+ 상위 클래스에서 제공하는 메서드를 더 나은 버전으로 개선할 수 있음. (Override는 아니지만 동일한 기능 제공 가능)
+ 참조하고 있는 인스턴스 변수를 변경해 프로그램을 동적으로 변경할 수 있음
+ 상위 클래스의 메서드 형태와 관계없이 유연하게 하위 클래스의 메서드를 정의할 수 있음. (상위 클래스에서는 String 반환 값을 가지지만 하위 클래스에서는 Integer를 가지도록 할 수 있음)
◈ Assosiation이란?
- 하나의 인스턴스가 다른 인스턴스로 메시지를 보내는 기능
- 일반적으로 포인터 또는 참조 인스턴스 변수로 구현되지만 메서드 매개변수 또는 로컬 변수 작성으로도 구현될 수 있음.
- 모든 오브젝트가 각자의 라이프 사이클을 가지고 있고, 어떤 오브젝트가 다른 오브젝트를 소유하지는 않은 경우 (객체 각각이 생성/소멸을 독립적으로 함)
- 많은 학생이 한 선생님에게 Association을 가질 수 있고, 한 학생이 여러 선생님에게 Association을 가질 수 있음. (누가 누구를 소유하지는 않는 관계)
◈ Aggregation이란?
- Association의 특별한 경우이며, 모든 오브젝트가 각자의 라이프 사이클을 가지고 있으며, 한 객체가 다른 객체를 소유하고 있는 경우
- 선생님이 어떤 부서에 Aggregation 되어 있다고 할 때, 소속된 관계이기 때문에 한 선생님이 여러 부서에 Aggregation이 될 수는 없음. 그렇다고 부서가 소멸될 때 선생님도 소멸되는 것은 아님.
- 전체 / 부분 관계를 말함. 이것은 인스턴스가 순환 Aggregation 관계를 가질 수 없다는 것을 제외하면 Association과 동일함.
◈ Delegation이란?
- 특정 클래스의 역할을 또 다른 클래스에게 위임하는 구조
- 상속 관계는 정적인 관계로 컴파일 시간에 모든 관계가 정해지고 변경될 수 없지만 위임 관계는 동적인 관계로 런타임 시간 동안 관계가 변경될 수 있음.
> Delegation 사용 이유
- 상속 대신 Delegation과 Interface를 사용함으로써 클래스는 훨씬 더 유연해짐
- 여러 클래스에서 겹치는 메서드를 줄이기 위해 사용
- 하나의 독립적인 행동이 필요하지만, 미래에 이 행동이 바뀔 수 있는 상황에서 사용
- 하나의 상속된 형태를 위임과 함께 사용하기 위해 사용
> Delegation의 문제점
- DRY(Don't Repeat Yourself) 원칙 위반 → 위임하는 객체의 메서드가 늘어날 수록 위임을 사용하는 클래스의 중복 코드가 늘어남. 메서드 내부에서 메서드명이 거의 비슷한 Delegation 객체 메서드를 호출하는 형태들.
- OCP(Open Closed Principle) 원칙 위반 → 위임하는 객체가 수정되면 위임을 사용하는 클래스도 수정되어야 함.
◈ 컬렉션 프레임워크(Collection framework)
- 자바에서 컬렉션 프레임워크란 다수의 데이터들을 쉽고 효과적으로 처리할 수 있는 표준화된 방법을 제공하는 클래스의 집합을 의미함. (데이터를 저장하는 자료구조와 데이터를 처리하는 알고리즘을 구조화하여 클래스로 구현해 놓은 것)
- 자바의 인터페이스(interface)를 사용하여 구현됨.
> 컬렉션 프레임워크 주요 인터페이스
① List interface
특징 : 순서가 있는 데이터의 집합, 데이터의 중복 허용.
구현 클래스 : Vector, ArrayList, LinkedList, Stack, Queue
② Set interface
특징 : 순서가 없는 데이터의 집합, 데이터의 중복 허용하지 않음.
구현 클래스 : HashSet, TreeSet
③ Map interface
특징 : 키와 값의 한 쌍으로 이루어지는 데이터의 집합, 순서 없음. 키는 중복 허용하지 않지만 값은 중복될 수 있음.
구현 클래스 : HashMap, TreeMap, Hashtable, Properties
- List와 Set 인터페이스는 모두 Collection 인터페이스를 상속받지만, 구조상의 차이로 인해 Map 인터페이스는 별도로 정의됨.
- Collection 인터페이스에서 제공하는 주요 인터페이스
↳ boolean add(E e), void clear(), boolean contains(Object o), boolean equals(Object o), boolean isEmpty(), Iterator<E> iterator(), boolean remove(Object o), int size(), Object[] toArray()
> Collection이 Cloneable, Serializable 인터페이스를 extends 하지 않는 이유는?
- Collection은 모든 컬렉션 클래스의 루트 인터페이스이기 때문. Collection 인터페이스가 Cloneable/Serializable 인터페이스를 확장하는 경우 복제 가능하고 직렬화 가능한 인터페이스를 구현하기 위해 이 인터페이스의 모든 구체적인 표현을 요구함. 구체적인 구현 클래스에 자유를 주기위해 Collection 인터페이스는 Cloneable/Serializable 인터페이스 확장하지 않음
◈ Vector와 ArrayList의 차이
공통점 : 동적인 배열을 다루는 컬렉션 객체
차이점 : Vector는 동기화된 상태(Thread-safe)이지만 ArrayList는 동기화 안된 상태. 동기화 여부로 인해 Vector는 상대적으로 속도가 느리고, ArrayList는 속도가 빠름. 멀티스레드 환경인 경우 Vector, 아닐경우 ArrayList 사용.
◈ ArrayList와 LinkedList의 차이
공통점 : 둘 다 Java에서 제공하는 List 인터페이스를 구현한 Collection 구현체
차이점 : ArrayList는 내부적으로 데이터를 배열로 관리하고 데이터 추가/삭제시 임시 배열을 생성해 데이터를 복사함. LinkedList는 노드 단위로 내부 데이터를 관리함. 데이터 검색시 ArrayList는 인덱스가 있어서 유리함. LinkedList는 모든 노드를 순회해야하므로 불리함. 데이터 추가/삭제시에는 임시배열을 만드는 ArrayList가 불리. LinkedList는 불필요한 데이터 복사가 없어서 유리.
◈ HashMap과 Hashtable의 차이
- HashMap은 널 키와 널 값을 허용하지만 Hashtable은 널 키와 널 값을 허용하지 않음
- HashMap은 동기화되지 않지만 Hashtable은 동기화됨. 따라서 HashMap은 단일 쓰레드 환경에서 적합하지만 Hashtable은 다중 쓰레드 환경에서 적합함
◈ Comparable과 Comparator
- 객체의 정렬 기준을 명시하는 두 가지 방법
▷ Comparable 인터페이스
- 정렬 수행 시 기본적으로 적용되는 정렬 기준이 되는 메서드를 정의하는 인터페이스이다. (자바에서 제공되는 정렬 가능한 클래스들은 모두 Comparable 인터페이스를 구현하고 있으며, 정렬시에 이에 맞게 정렬이 수행됨)
- 정렬할 객체에 Comparable interface를 implements 후, compareTo() 메서드를 오버라이드하여 구현함
- compareTo() 메서드 작성시 "현재 객체 < 파라미터로 넘어온 객체" → 음수 리턴, "현재 객체 == 파라미터로 넘어온 객체" → 0 리턴, "현재 객체 > 파라미터로 넘어온 객체" → 양수 리턴. 음수 또는 0일때 객체의 자리가 그대로 유지되며, 양수인 경우 두 객체의 자리가 바뀜
> Arrays.sort() 와 Collections.sort 의 차이
- Arrays.sort() : 배열 정렬인 경우 사용. ( TimSort(Merge Sort + Insertion Sort), Quick Sort )
- Collections.sort() : List Collection 정렬인 경우 사용.
▷ Comparator 인터페이스
- 정렬 가능한 클래스(Comparable 인터페이스를 구현한 클래스)들의 기본 정렬 기준과 다르게 정렬하고 싶을때 사용하는 인터페이스.
- 주로 익명 클래스로 사용되며 기본적인 정렬방법인 오름차순 정렬을 내림차순으로 한다거나 할때 사용.
- Comparator 인터페이스를 implements 후 compare() 메서드를 오버라이드한 클래스를 따로 작성한다.
- Arrays.sort(array, myComparator), Collections.sort(array, myComparator) 등과 같이 사용.
- Priority Queue 생성자의 두번째 인자로 Comparator 인터페이스를 받을 수 있다고 함. (우선순위 큐는 우선순위 힙을 기반으로 한 unbounded queue로써 그 요소들은 순서가 있음)
◈ Iterator란?
- 반복자 인터페이스는 컬렉션을 반복할 수 있는 메서드를 제공함. 각 Java collection은 iterator 메서드를 포함하며, 해당 메서드들이 return 하는 것은 Iterator 객체이다. iterator는 반복하는 가운데 collection의 요소를 제거할 수 있음.
> Iterator와 ListIterator의 차이
- Iterator는 Set, List collection을 traverse하는데 사용할 수 있으나, ListIterator는 리스트에 대한 traverse만 가능함.
- Iterator는 collection을 traverse 할때 forward 방향으로만 할 수 있지만, ListIterator는 양방향으로 가능.
- ListIterator는 Iterator 인터페이스를 implements하며, 추가 기능을 포함함.
> Enumeration과 Iterator 인터페이스의 차이
- Enumeration은 논리적인 커서가 존재하며 커서를 이동하면서 데이터를 꺼내옴.
- Enumeration은 Iterator에 비해 빠르며 적은 메모리를 사용함. 그러나 다른 쓰레드는 현재 Iterator가 통과하는 collection 객체를 수정할 수 없으므로 Iterator가 Enumeration보다 안전함.
◈ fail-fast와 fail-safe
- fail-fast 방식은 동작중 오류가 발생하면 바로 오류를 알리고 작업을 중단하지만, fail-safe 방식은 동작중 오류가 발생해도 작업을 중단하지 않고 진행.
- java.util 패키지의 모든 collection 클래스는 fail-fast인 반면, java.util.concurrent는 fail-safe이다.
- fail-fast iterator는 Exception을 throw하지만, fail-safe는 throw하지 않음.
◈ Framework란?
- 특정 형태의 소프트웨어 문제를 해결하기 위해 상호 협력하는 클래스 프레임과 인터페이스 프레임의 집합
- 특정한 틀을 만들어 놓고 거기에 살을 붙여 놓음으로써 프로그램을 만들어 작업시간을 줄여줌
- 프레임워크는 특정 개념들의 추상화를 제공하는 여러 클래스나 컴포넌트로 구성됨
- 프레임워크는 이렇게 추상적인 개념들이 문제를 해결하기 위해 같이 작업하는 방법을 정의함
- 좀 더 높은 수준에서 패턴을 조작함
- 프레임워크가 중요한 이유 : 객체지향 개발을 하게 되면서 개발자의 취향에 따라 다양한 프로그램이 나오게 됨. 프로그램 개발에 투입되는 개발자도 점점 늘어남에 따라 전체 시스템의 통합성, 일관성이 부족하게 되었기 때문. 개발자의 자유를 제한하기 위해 프레임워크 도입.
> 프레임워크가 가지는 특징
- 개발자들이 따라야할 가이드라인을 가짐
- 개발할 수 있는 범위가 정해져 있음
- 개발자를 위한 다양한 도구가 지원됨
> 프레임워크의 장단점
- 장점 : 개발 시간을 줄일 수 있고 오류로부터 자유로울 수 있음
- 단점 : 프레임워크에 너무 의존하면 개발 능력이 떨어져서 프레임워크 없이 개발하는것이 불가능해짐
◈ Spring Framework
- Java 플랫폼을 위한 오픈소스 애플리케이션 프레임워크
- 자바 엔터프라이즈 개발을 편하게 해주는 오픈소스 경량급 애플리케이션 프레임워크
- 자바 개발을 위한 프레임워크로 종속 객체를 생성해주고 조립해주는 도구
- 자바로 된 프레임워크로 자바 SE로 된 자바 객체를 자바 EE에 의존적이지 않게 연결해주는 역할
> Spring 특징
- 크기와 부하의 측면에서 경량
- 제어 역행이라는 기술을 통해 애플리케이션의 느슨한 결합을 도모함. 컨트롤의 제어권이 사용자가 아니라 프레임워크에 있어서 필요에 따라 스프링에서 사용자의 코드를 호출함.
- 관점 지향 프로그래밍을 위한 풍부한 지원. 트랜잭션이나 로깅, 보안과 같이 여러 모듈에서 공통적으로 사용하는 기능의 경우 해당 기능을 분리하여 관리할 수 있음.
- 애플리케이션 객체의 생명주기와 설정을 포함하고 관리한다는 점에서 일종의 컨테이너라고 할 수 있음
- 간단한 컴포넌트로 복잡한 애플리케이션을 구성하고 설정할 수 있음.
- POJO(Plain Old Java Object) 방식의 프레임워크. 일반적인 J2EE 프레임워크에 비해 구현을 위해 특정한 인터페이스를 구현하거나 상속을 받을 필요가 없어 기존에 존재하는 라이브러리등을 지원하기에 용이하고 객체가 가벼움.
- 의존성 주입(DI, Dependency Injection)을 지원. 각각의 계층이나 서비스들 간에 의존성이 존재할 경우 프레임워크가 서로 연결시켜줌.
- 스프링은 영속성과 관련된 다양한 서비스를 지원. MyBatis나 Hibernate 등 이미 완성도가 높은 데이터베이스 처리 라이브러리와 연결할 수 있는 인터페이스를 제공함
- 스프링은 확장성이 높음. 스프링 프레임워크에 통합하기 위해 간단하게 기존 라이브러리를 감싸는 정도로 스프링에서 사용이 가능하기 때문에 수많은 라이브러리가 이미 스프링에서 지원되고 있고 스프링에서 사용되는 라이브러리를 별도로 분리하기도 용이함.
> Spring MVC 구조의 처리 과정
① DispatcherServlet : 어플리케이션으로 들어오는 모든 Request를 받는 관문. Request를 실제로 처리함. Controller에 전달하고 그 결과값을 받아서 View에게 전달하여 적절한 응답을 생성할 수 있도록 흐름을 제어함
② HandlerMapping : Request URL 각각 어떤 Controller가 실제로 처리할 것인지 찾아주는 역할
③ Controller : Request를 직접 처리한 후 그 결과를 다시 DispatcherServlet에게 돌려줌.
④ Model and View : Controller가 처리한 결과와 그 결과를 보여줄 View에 관한 정보를 담고있는 객체
⑤ View Resolver : View 관련 정보를 갖고 실제 View를 찾아주는 역할을 함
⑥ View : Controller가 처리한 결과값을 보여줄 View 생성
◈ Model 1, Model 2, MVC 패턴에 대한 설명
▷ Model 1
- 뷰와 로직을 모두 JSP 페이지 하나에서 처리하는 구조
- 구조가 단순해서 익히기 쉬우나, 코드가 복잡해서 유지보수가 어려움
▷ Model 2
- 모든 처리를 JSP 페이지 하나가 담당하는 것과는 달리 JSP 페이지와 서블릿, 로직을 위한 클래스가 나뉘어 브라우저 요청을 처리함. 요청이 들어오면 그에 대한 로직 처리는 이를 처리할 Model인 서비스 클래스 혹은 자바빈이 담당하고, 요청 결과는 유저에게 결과를 보여줄 View단인 JSP에 출력되며, 이를 위한 모든 흐름제어는 Controller인 서블릿에서 담당.
▷ MVC 모델
- Model, View, Controller 패턴을 웹 개발에 도입한 구조이며, Model 2와 MVC 패턴의 형태는 같은 형태를 가짐.
- 모델과 뷰를 구분하기 위해 사용
* Model : 소프트웨어 응용과 그와 관련된 고급 클래스 내의 논리적 데이터 기반 구조를 표현함.
* View : 사용자 인터페이스 내의 구성요소들을 표현하는 클래스들의 집합
* Controller : 모형과 뷰를 연결하고 있는 클래스들을 대표하며, 모형과 뷰 내의 클래스들간에 통신하는데 사용. 장점으로는 뷰와 로직을 구분하기 때문에 코드가 복잡하지 않으며, 유지보수가 용이함.
◈ Servlet 라이프 사이클
① 사용자가 서블릿에 대한 링크(URL)을 클릭
② 컨테이너는 요청된 Request가 서블릿이라는 것을 간파하고 두개의 객체를 생성함 → Response, Request
③ 접수한 요청의 URL을 분석하여 어떤 서블릿을 요청했는지 파악. 그 다음 해당 서블릿 스레드를 생성하여 Request, Response 객체 참조를 넘김
④ 컨테이너는 서블릿 service() 메서드 호출. 브라우저에 지정한 방식에 따라 doGet()을 호출할지 doPost()를 호출할지 결정함. 클라이언트가 HTTP GET 메서드를 날렸다면 service() 메서드는 서블릿의 doGet() 메서드를 호출함. 호출할 때 Request와 Response 객체를 인자로 넘김.
⑤ 서블릿은 클라이언트에게 응답을 작성하기 위해 Response 객체를 사용. 작업 완료 후, Response에 대한 제어는 컨테이너에게 넘어감.
⑥ service() 메서드가 끝나면, 쓰레드를 소멸하거나 컨테이너가 관리하는 쓰레드 풀로 돌려보냄. 그 다음 Request와 Response 객체를 가비지 컬렉션이 될 준비를 하고, 이 객체에 대한 참조는 이제 사라짐. 마지막으로 클라이언트는 서버로부터 응답을 받음
◈ Session과 Cookie
▷ Session : 특정 웹사이트에서 사용자가 머무르는 기간 또는 한명의 사용자의 방문을 의미. Cookie와 달리 개인 아이디와 비밀번호같은 웹사이트의 데이터는 Server에 저장되거나 웹브라우저 캐시에 저장되어 브라우저가 닫히거나 서버에서 삭제시 사라짐
▷ Cookie : 사용자 정보를 유지할 수 없다는 HTTP 고유의 한계를 극복할 수 있는 방법으로 인터넷 웹사이트의 방문 기록을 남겨 사용자와 웹사이트 사이를 매개해 주는 정보. 이러한 Cookie는 인터넷 사용자가 특정 웹서버에 접속할 때, 생성되는 개인 아이디와 비밀번호, 방문한 사이트의 정보를 담은 임시 파일로써, Server가 아닌 Client에 텍스트 파일로 저장되어 있어 다음에 해당 웹서버를 찾을 경우 웹서버에서는 그가 누구인지, 어떤 정보를 주로 찾았는지 등을 파악할때 사용함.
◈ 변수 명명법
- 헝가리안 표기법 : 자료형을 식별자에 같이 포함
- 파스칼 표기법 : 식별자가 한 단어 또는 여러 단어로 언더바 사용없이 조합. 각 단어의 첫 문자만 대문자로 함.
- Camel 표기법 : 모든 단어를 공백없이 언더바 없이 조합. 첫단어의 첫글자는 소문자.
- Snake 표기법 : 단어 사이에 언더바를 사용하여 조합.
◈ DAO와 DTO
- DAO : Data Access Object의 약자로 간단히 데이터베이스의 데이터에 접근을 위한 객체. DB에 접근을 하기 위한 로직과 비즈니스 로직을 분리하기 위해서 사용함. DB를 사용해 데이터를 조회하거나 조작하는 기능을 전담하도록 만든 오브젝트를 말함.
- DTO : Data Transfer Object의 약자로 VO(Value Object)로 바꿔 말할 수 있으며 계층간 데이터 교환을 위한 JavaBean을 말함. 여기서 말하는 계층이란 Controller, View, Business layer, Persistence layer(=DB와 연결하는 지점) 등을 말하며 각 계층간 데이터 교환을 위한 객체를 DTO라고 함. VO는 read-only 속성을 가짐.
댓글