주니어 개발자 기술 면접 리스트
- -
오늘은 주니어 개발자의 기술 면접에 나오는 내용에 대해 정리하여 볼까 한다.
내가 아는 것과 설명하는 것은 또 다른 영역이기 때문에 중요한 핵심만 뽑아서 이야기 할 수 있도록
잘 정리해두록 하자.
JAVA 기본
OOP란
객체지향 프로그래밍(Object-Oriented Programming, OOP)은 프로그래밍 패러다임 중 하나로, 현실 세계의 사물이나 개념을 객체로 모델링하여 소프트웨어를 개발하는 방법입니다. 이 접근 방식은 프로그램을 보다 유연하고, 수정이 용이하며, 이해하기 쉽게 만드는 데 중점을 둡니다. 객체지향 프로그래밍의 핵심은 데이터(속성)와 그 데이터를 처리하는 데 필요한 연산(메서드)을 하나의 '객체'라는 단위로 묶고, 이러한 객체들이 서로 상호작용하면서 프로그램을 구성하는 것입니다.
대표적인 특징으로 추상화, 상속, 캡슐화, 다형성이 있습니다.
- 클래스와 객체: 클래스는 객체를 생성하기 위한 템플릿이며, 객체는 클래스의 인스턴스입니다. 클래스는 객체의 구조와 행동을 정의하고, 객체는 클래스에 정의된 속성(상태)과 메서드(행위)를 가집니다.
- 상속: 한 클래스가 다른 클래스의 속성과 메서드를 상속받아 사용할 수 있게 하는 기능입니다. 이를 통해 코드의 재사용성을 높이고, 중복을 줄일 수 있습니다.
- 캡슐화: 객체의 세부 구현을 외부로부터 숨기고, 외부에서는 객체의 기능만을 사용할 수 있도록 하는 기능입니다. 이는 객체의 내부 구현을 변경해도 외부에 영향을 미치지 않게 하여, 유지 보수성을 향상시킵니다.
- 다형성: 같은 이름의 메서드가 객체에 따라 다른 행위를 할 수 있게 하는 기능입니다. 이는 메서드 오버로딩(같은 이름의 메서드를 매개변수의 타입이나 개수를 달리하여 여러 개 정의)과 오버라이딩(상속받은 메서드의 기능을 변경)을 통해 구현됩니다.
- 추상화: 복잡한 현실 세계를 단순화시키는 과정입니다. 필요한 정보와 기능만을 추출하여 프로그램에 모델링함으로써, 관리하기 쉽고 이해하기 쉬운 프로그램을 만들 수 있습니다.
객체 지향적 설계 원칙의 종류
- SRP(Single Responsibility Principle) : 단일 책임 원칙
클래스는 단 하나의 책임을 가져야 하며 클래스를 변경하는 이유는 단 하나의 이유이어야 합니다. - OCP(Open-Closed Principle) : 개방-폐쇄 원칙
확장에는 열려 있어야 하고 변경에는 닫혀 있어야 합니다. - LSP(Liskov Substitution Principle) : 리스코프 치환 원칙
서브타입은 언제나 그것의 기반 타입으로 교체할 수 있어야 합니다. - ISP(Interface Segregation Principle) : 인터페이스 분리 원칙
인터페이스는 그 인터페이스를 사용하는 클라이언트를 기준으로 분리해야 합니다. - DIP(Dependency Inversion Principle) : 의존 역전 원칙
고수준 모듈은 저수준 모듈에 의존하면 안 되며, 둘 다 추상화에 의존해야 합니다.
프레임워크와 라이브러리의 차이점
라이브러리: 개발자가 애플리케이션의 흐름을 제어하며, 필요할 때 특정 기능을 수행하기 위해 라이브러리를 호출합니다. 즉, 라이브러리는 도구 모음으로, 개발자가 선택하여 사용합니다.
프레임워크: 프레임워크가 애플리케이션의 흐름을 제어하고, 개발자는 프레임워크가 제공하는 특정 규칙이나 함수를 구현하여 사용합니다. 프레임워크는 일종의 작업 틀로, 애플리케이션 개발 시 구조와 흐름을 제공합니다.
런타임 vs 컴파일 타임
컴파일 타임
정의: 컴파일 타임은 소스 코드가 기계어 코드로 변환되는 과정이 발생하는 시간을 의미합니다. 이 단계에서는 프로그램의 소스 코드가 컴파일러에 의해 분석되고, 실행 가능한 형태로 번역됩니다.
중요 포인트: 컴파일 타임에는 문법 오류, 타입 체크, 변수 선언 확인 등 소스 코드의 정적 분석이 이루어집니다. 즉, 프로그램을 실행하기 전에 코드 내의 오류를 찾아내고 수정할 수 있습니다.
예시: C, C++, Java와 같은 컴파일 언어에서 .java 또는 .c 파일을 컴파일하여 실행 파일이나 바이트코드를 생성하는 과정.
런타임
정의: 런타임은 프로그램이 실제로 실행되고 있는 동안의 시간을 의미합니다. 이 단계에서 프로그램은 사용자의 입력을 받아 처리하고, 결과를 출력하는 등의 동적인 작업을 수행합니다.
중요 포인트: 런타임에는 메모리 할당, 예외 처리, 입력/출력 처리 등 프로그램의 실행과 직접적으로 관련된 작업이 이루어집니다. 이 시간 동안에 발생하는 오류를 런타임 오류라고 합니다.
예시: 인터프리터 언어인 Python에서 스크립트를 실행할 때 또는 Java 프로그램이 JVM 위에서 실행될 때의 과정.
차이점
컴파일 타임은 코드가 컴파일되는 단계로, 주로 코드의 문법 검사와 정적 분석이 이루어집니다. 오류는 프로그램 실행 전에 발견되고 수정됩니다.
런타임은 컴파일된 코드가 실행되는 단계로, 프로그램의 동적인 동작이 수행됩니다. 런타임에 발생하는 오류는 프로그램 실행 중에만 확인할 수 있습니다.
인터프리터 방식과 컴파일 방식이란?
인터프리터란?
인터프리터는 소스 코드를 한 번에 한 줄씩 읽어서 바로 실행하는 프로그램입니다. 이 방식은 프로그램을 실행하기 위해 별도의 컴파일 과정을 거치지 않으므로, 코드를 작성하고 바로 실행 결과를 볼 수 있는 장점이 있습니다. Python, Ruby, JavaScript와 같은 언어들이 인터프리터 방식을 사용합니다.
컴파일러란?
컴파일러는 소스 코드 전체를 먼저 기계어 코드로 번역하고, 이 번역된 코드를 실행 파일로 만드는 프로그램입니다. 이 과정을 통해 생성된 실행 파일은 직접 실행될 수 있습니다. C, C++, Java와 같은 언어들이 컴파일러 방식을 사용합니다.
함께 사용되는 경우
일부 언어는 컴파일러와 인터프리터를 함께 사용합니다. 예를 들어, Java는 소스 코드를 바이트코드로 컴파일하는 컴파일러(javac)를 사용하고, 이 바이트코드를 실행하는 인터프리터인 JVM(Java Virtual Machine)을 사용합니다. 이런 경우, 컴파일 단계에서는 플랫폼 독립적인 바이트코드가 생성되고, 실행 단계에서는 해당 플랫폼에 맞는 인터프리터가 바이트코드를 실행합니다.
Python과 같은 언어에서도, 소스 코드는 때로 사전에 바이트코드로 컴파일되어 .pyc 파일로 저장되며, 이후 이 바이트코드는 Python의 인터프리터에 의해 실행됩니다.
따라서, 인터프리터만 있는 것이 아니라, 컴파일러와 인터프리터가 각각 또는 함께 사용될 수 있으며, 이는 언어의 설계와 목적에 따라 달라집니다.
JVM이 무엇인가?
JVM은 자바 가상 머신(Java Virtual Machine)의 약자로, 자바 바이트코드(.class 파일)를 운영 체제에 구애받지 않고 실행할 수 있게 해주는 가상 머신이다. 자바 애플리케이션은 JVM 위에서 돌아가므로, 자바 프로그램이 다양한 운영 체제에서도 동일하게 작동할 수 있게 해준다. JVM은 바이트코드를 실제 CPU가 이해할 수 있는 기계어로 변환하는 인터프리터 또는 JIT(Just-In-Time) 컴파일러를 포함한다. 또한, 메모리 관리, 가비지 컬렉션과 같은 여러 가지 시스템 리소스 관리 기능을 제공한다.
JVM의 실행 구조
1. 클래스 로딩(Class Loading)
- 로딩(Loading): JVM은 클래스 로더(Class Loader)를 사용하여 .class 파일(바이트코드)을 메모리에 로드합니다. 이 단계에서 클래스 로더는 파일 시스템이나 네트워크 소스 등에서 클래스 파일을 찾고, 이를 읽어서 JVM 내의 런타임 데이터 영역에 배치합니다.
- 링크(Linking): 로드된 클래스나 인터페이스는 검증(Verify), 준비(Prepare), 그리고 (선택적으로) 해석(Resolve)의 과정을 거칩니다.
검증(Verification): 클래스 파일의 형식이 유효한지 검사하여 안전성을 확보합니다.
준비(Preparation): 클래스 변수와 기본값에 필요한 메모리를 할당합니다.
해석(Resolution): 심볼릭 메모리 참조를 실제 참조로 변환합니다. - 초기화(Initialization): 클래스 변수들에 대한 적절한 값들로 초기화를 수행합니다. 이는 주로 정적 변수들에 할당된 값들에 대한 초기화 코드가 실행되는 단계입니다.
2. 실행(Execution)
- 인터프리트 실행: JVM은 클래스의 메인 메서드(public static void main(String[] args))를 찾아서 실행을 시작합니다. 초기에는 JVM 인터프리터가 바이트코드를 한 줄씩 읽어서 실행합니다.
- JIT 컴파일: 런타임 중에 JVM은 자주 실행되는 "핫스팟" 코드를 식별하고, 이를 네이티브 코드로 컴파일하여 성능을 향상시키는 Just-In-Time(JIT) 컴파일러를 사용할 수 있습니다. JIT 컴파일된 코드는 바이트코드가 실행될 때마다 인터프리트하는 것보다 훨씬 빠르게 실행됩니다.
3. 실행 시 데이터 관리
- 런타임 데이터 영역 관리: JVM은 메소드 실행을 위한 스택 공간, 객체와 배열을 위한 힙 공간, 클래스와 메소드에 대한 메타 데이터를 저장하는 메소드 영역 등을 관리합니다.
- 가비지 컬렉션(Garbage Collection, GC): JVM은 더 이상 사용되지 않는 객체들을 자동으로 관리하고 메모리를 회수합니다. GC는 프로그램의 실행 중에 주기적으로 발생합니다.
4. 종료(Shutdown)
- 종료: 모든 main 메서드가 종료되고, 더 이상 실행 중인 스레드가 없거나 System.exit() 호출과 같이 JVM 종료 조건이 충족되면 JVM은 종료됩니다.
- 정리 작업: JVM은 사용하던 자원들을 해제하고, 필요한 경우 최종화(finalization) 작업을 수행한 후 종료됩니다.
인코딩 디코딩이란?
인코딩과 디코딩은 데이터 전송, 저장, 변환에 중요한 역할을 한다.
인코딩은 데이터를 특정 형식이나 코드로 변환하는 과정을 말한다.
예를 들어, 문자열을 컴퓨터가 이해할 수 있는 이진 코드로 변환하는 것이다.
디코딩은 인코딩된 데이터를 원래의 형태로 복원하는 과정이다. 예를 들어, 이진 코드로 인코딩된 문자열을 다시 사람이 읽을 수 있는 텍스트로 변환하는 것이다.
인코딩의 장점:
- 데이터 호환성 향상: 다양한 시스템이나 애플리케이션 간의 호환성을 보장하기 위해 인코딩을 사용한다. 예를 들어, UTF-8 인코딩은 전 세계의 모든 문자를 포함할 수 있어 글로벌한 커뮤니케이션에 필수적이다.
- 데이터 보안 강화: 특정 인코딩 방식을 사용하여 데이터를 암호화함으로써 정보를 보호할 수 있다. 이는 데이터가 노출되더라도 원본 데이터의 내용을 쉽게 알아볼 수 없게 한다.
- 데이터 압축: 인코딩은 데이터를 압축하여 저장 공간을 절약하고, 데이터 전송 시간을 단축시킬 수 있다.
servlet과 jsp 차이
Servlet: 자바 코드로 웹 페이지를 동적으로 생성하는 기술입니다.
클라이언트의 요청에 대한 로직을 처리하고, 결과를 HTML 형태로 변환해 응답합니다.
JSP HTML 내에 자바 코드를 삽입하여 웹 페이지를 동적으로 생성하는 기술입니다.
HTML 페이지입니다. 페이지 내에 자바 코드를 삽입할 수 있는 특수 태그가 있습니다. 서버에서는 이 JSP 파일을 서블릿으로 변환하여 실행합니다.
직렬화(serialization)란?
자바에선 입출력에 바이트 스트림과 문자 스트림 두 가지를 지원한다.
바이트 스트림은 플랫폼 독립적이다. 자바는 "한 번 작성하면, 어디서든 실행된다(Write Once, Run Anywhere - WORA)"의 철학을 가지고 있는데, 바이트 스트림으로 직렬화함으로써 다양한 플랫폼에서 자바 애플리케이션을 실행할 때 호환성 문제 없이 객체의 상태를 저장하고 전송할 수 있다
자바에서 직렬화(Serialization)는 객체의 상태를 바이트 스트림으로 변환하는 과정을 말하며, 이렇게 변환된 바이트 스트림은 파일 시스템에 저장하거나 네트워크를 통해 다른 자바 시스템에 전송할 수 있다.
직렬화된 객체는 나중에 역직렬화(Deserialization)를 통해 다시 원래의 상태로 복원될 수 있다.
박싱과 언박싱이란?
자바에서 박싱은 기본 타입의 데이터를 해당 기본 타입의 래퍼 클래스로 변환하는 과정을 말한다. 예를 들어, int 타입의 변수를 Integer 클래스의 객체로 변환하는 것이다. 이러한 변환은 자동으로 일어날 수 있으며, 이를 오토박싱이라고 한다.
언박싱은 이와 반대로, 래퍼 클래스의 객체를 기본 타입의 데이터로 변환하는 과정을 말한다. Integer 객체를 int 타입으로 변환하는 것이 예시다. 이 과정 역시 자동으로 수행될 수 있으며, 이를 오토언박싱이라고 한다.
박싱 과정에서는 기본 타입의 데이터가 힙 영역에 할당되는 객체로 변환되기 때문에 메모리 사용량이 늘어나고, 성능상의 오버헤드가 발생할 수 있다. 언박싱 과정에서도 타입 변환에 따른 추가적인 처리가 필요하므로, 성능 저하의 원인이 될 수 있다.
타입 캐스팅이란?
- 업캐스팅
자식 클래스에서 부모 클래스의 참조로 변환하는 것 - 다운캐스팅
반대로, 부모 클래스 타입의 참조를 자식 클래스 타입의 참조로 변환하는 것은 "다운캐스팅"
HashMap vs HashTable vs ConcurrentHashMap
- HashMap
- 동기화 지원 x
- key, value에 null을 입력할 수 있습니다.
- HashTable
- 모든 메서드가 동기화 되어있다.
- 멀티 스레드 환경에서 스레드세이프 함
- key, value에 null을 허용하지 않습니다.
- ConcurrentHashMap
- 여러 스레드가 동시에 읽고 쓸수 있도록 하여 HashTable 보다 높은 동시 읽기/쓰기 지원
- key, value에 null을 허용하지 않습니다.
접근 제어자
- public : 어디서든 접근이 가능합니다.
- protected : 동일 패키지 혹은 상속받은 외부 패키지 클래스에서 사용 가능합니다.
- (default) : 동일 패키지 내에서만 접근 가능합니다.
- private : 해당 클래스 내에서만 접근 가능합니다.
interface vs abstract
- interface(인터페이스)
- 할수 있는 것에 대해 정의(기능)
- 다중 상속
- 상태 정보(필드) 가질 수 없음, 즉 변경 불가한 상수만 선언 가능
- 생성자, 일반 변수를 가질 수 없음
- 팀별 협업시 추상메서드를 통해 결과를 예측하고 작업이 가능함(구현 객체의 동일성 보장)
- abstract(추상 클래스)
- 무엇인가 를 정의
- 다중 상속 불가
- 추상 메서드 1개 이상, 일반 변수, 일반 메서드 선언 가능
- 생성자, 일반 변수 가질 수 있음
- 메서드의 부분 구현이 가능(부분 구현된 메서드를 상속받아 확장시키기 위함)
String vs StringBuffer vs StringBuilder
- String
- immutable(불변)
- 객체를 한 번 할당할시 메모리 공간에 변동이 없습니다.(할당시 Heap String Pool영역에 생성되어 그 값을 계속 사용합니다.)
- 동기화를 신경쓰지 않아도 됩니다.
- 엄청나게 많은 문자열을 선언 및 연산할 시 성능저하를 고려해야합니다.
- StringBuffer
- mutable(가변)
- 각 메서드별로 Synchronized Keyword가 존재합니다.
- 멀티스레드 환경에서도 동기화를 지원합니다.(thread-safe)
- StringBuilder
- mutable(가변)
- 동기화를 지원하지 않습니다.
Static이란?
자바에서 static 키워드는 주로 변수, 메서드, 블록 또는 중첩 클래스에 사용되며, 이를 클래스의 인스턴스가 아닌 클래스 자체에 속하도록 만든다. static으로 선언된 멤버는 해당 클래스의 모든 인스턴스에 의해 공유되며, 클래스가 메모리에 로드될 때 한 번만 생성된다. 따라서 인스턴스를 생성하지 않고도 static 멤버에 접근할 수 있다.
Static의 의미
- 클래스 레벨의 멤버: static 키워드가 적용된 필드나 메소드는 인스턴스 레벨이 아닌 클래스 레벨에 존재합니다. 이는 객체를 생성하지 않고도 접근할 수 있다는 것을 의미합니다.
- 공유 자원: static으로 선언된 변수는 해당 클래스의 모든 인스턴스에 의해 공유됩니다. 즉, 하나의 변수 값이 변경되면, 해당 클래스의 모든 인스턴스에 영향을 미칩니다.
- 정적 초기화 블록: static 키워드는 초기화 블록에도 사용될 수 있으며, 이는 클래스가 메모리에 로드될 때 단 한 번만 실행됩니다.
장점
- 메모리 효율성: static 필드는 모든 인스턴스가 공유하기 때문에, 같은 값을 가진 여러 복사본을 생성할 필요가 없어 메모리 사용이 줄어듭니다.
- 접근 용이성: 객체를 생성하지 않고도 클래스 이름을 통해 직접 접근할 수 있어 사용하기 편리합니다. 이는 유틸리티 함수나 상수를 정의할 때 유용합니다.
- 싱글턴 패턴 구현: static 멤버를 활용하여 싱글턴(Singleton) 패턴을 구현할 수 있습니다. 이는 전역적으로 하나의 인스턴스만 유지되어야 하는 경우에 사용됩니다.
단점
- 전역 상태: static 변수는 기본적으로 전역 상태를 만듭니다. 이는 디버깅을 어렵게 만들고, 코드의 이해를 복잡하게 할 수 있습니다.
- 객체 지향 원칙 위배: 과도한 사용은 객체 지향 프로그래밍의 원칙, 특히 캡슐화와 다형성 원칙을 위배할 수 있습니다.
테스트의 어려움: static 멤버는 테스트를 어렵게 만들 수 있습니다. 특히, 단위 테스트 시 static 상태는 테스트 간에 유지되어 이전 테스트의 상태가 이후 테스트에 영향을 줄 수 있습니다.
try-with-resource란?
try-with-resources는 자동으로 자원을 해제해주는 기능입니다. try에서 선언된 객체가 AutoCloseable을 구현하였다면 Java는 try구문이 종료될 때 객체의 close() 메소드를 호출해 줍니다.
Synchronized(동기화)란?
자바에서 synchronized 키워드는 동기화를 위해 사용된다. 동기화는 멀티 스레딩 환경에서 여러 스레드가 동시에 같은 객체의 상태를 변경하지 못하도록 하여 데이터의 일관성과 동시성을 보장하는 메커니즘이다. 간단히 말해, 한 시점에 하나의 스레드만 특정 자원을 사용할 수 있도록 제한하는 것을 의미한다.
동기화의 필요성
멀티 스레딩 환경에서는 여러 스레드가 동시에 하나의 객체에 접근하여 상태를 변경할 수 있다. 이 경우, 스레드 간의 경쟁 상태(Race Condition)가 발생하여 예상치 못한 결과를 초래할 수 있다. 예를 들어, 두 스레드가 동시에 같은 계좌의 잔액을 변경하려고 할 때, 하나의 스레드가 변경한 내용이 다른 스레드에 의해 덮어쓰여질 수 있다. 이러한 문제를 방지하기 위해 동기화가 필요하다.
단점: 성능 저하,교착상태(Deadlook) 발생 가능
자바의 메모리 영역
- 메소드 영역(Method Area): 클래스 수준의 정보(클래스 이름, 부모 클래스 이름, 메소드 및 변수 정보 등)를 저장한다. 이 영역은 모든 스레드가 공유하며, JVM이 시작할 때 생성되어 각 클래스와 인터페이스의 메타데이터를 저장한다. 정적 변수도 이 영역에 저장된다.
- 힙 영역(Heap): 객체와 배열이 동적으로 할당되는 곳이다. 이 영역은 JVM이 관리하는 메모리 중 가장 큰 부분을 차지하며, 가비지 컬렉션의 주 대상이기도 하다. 모든 스레드에 의해 공유되며, 객체의 실제 인스턴스 데이터가 저장된다.
- 스택 영역(Stack): 각 스레드마다 독립적으로 존재하는 영역으로, 스레드가 시작될 때 생성된다. 메소드 호출 시 각 호출에 대한 스택 프레임이 생성되며, 로컬 변수, 기본 타입의 데이터, 메소드 호출에 대한 정보 등이 저장된다. 메소드의 호출이 종료되면 해당 스택 프레임은 스택에서 제거된다.
- PC 레지스터(PC Registers): 스레드마다 하나씩 존재하는 영역으로, 현재 실행 중인 JVM 명령어의 주소를 가진다. 이는 스레드가 어느 부분을 실행하고 있는지를 추적하는 데 사용된다.
- 네이티브 메소드 스택(Native Method Stack): 자바가 아닌 다른 언어로 작성된 네이티브 코드를 실행하기 위한 영역이다. 주로 C나 C++로 작성된 코드를 실행할 때 사용된다.
call by value vs claa by reference
- Call by Value
정의: Call by value 방식에서는 함수에 인자를 전달할 때 인자의 실제 값을 복사하여 함수의 매개변수에 할당합니다.
동작 방식: 함수 내에서 매개변수의 값을 변경해도, 그 변경이 원본 변수에 영향을 미치지 않습니다. 즉, 함수에 전달된 인자의 원본 값은 변경되지 않습니다.
특징: 이 방식은 보통 기본 데이터 타입(정수, 실수, 문자 등)의 전달에 사용됩니다. - Call by Reference
정의: Call by reference 방식에서는 함수에 인자를 전달할 때 인자의 메모리 주소를 전달합니다. 따라서 함수 내에서 매개변수를 통해 해당 메모리 주소에 직접 접근하고 변경할 수 있습니다.
동작 방식: 함수 내에서 매개변수의 값을 변경하면, 그 변경이 원본 변수에도 반영됩니다. 즉, 함수를 통해 전달된 인자의 원본 값 자체를 변경할 수 있습니다.
특징: 이 방식은 보통 객체, 배열 등과 같은 참조 데이터 타입의 전달에 사용됩니다.
Java에서의 적용
Java는 엄밀히 말하면 'call by value' 방식만을 사용합니다. 기본 데이터 타입(primitive types)은 직접 값이 복사되어 전달되며, 객체의 경우 객체의 참조(메모리 주소를 담고 있는 값)가 복사되어 전달됩니다. 객체의 참조가 복사되어 전달되기 때문에, 함수 내에서 참조를 통해 객체의 상태를 변경하면 원본 객체에도 그 변경이 반영됩니다. 그러나 전달된 참조 자체를 변경(다른 객체로 가리키도록 변경)하는 것은 원본 참조에 영향을 미치지 않습니다. 이는 참조의 값이 복사되어 전달되기 때문입니다.
따라서 Java에서는 "참조에 의한 호출"과 비슷한 효과를 볼 수 있지만, 기술적으로는 참조의 값(메모리 주소를 가리키는 값)이 복사되어 전달되는 "값에 의한 호출"입니다.
컬렉션 프레임워크란?
컬렉션 프레임워크(collection framework)란 다수의 데이터를 쉽고 효과적으로 처리할 수 있는 표준화된 방법을 제공하는 클래스의 집합을 의미합니다.
컬렉션 프레임워크에서는 데이터를 저장하는 자료 구조에 따라 다음과 같은 핵심이 되는 주요 인터페이스를 정의하고 있습니다.
- List 인터페이스
- Set 인터페이스
- Map 인터페이스
이 중에서 List와 Set 인터페이스는 모두 Collection 인터페이스를 상속받지만, 구조상의 차이로 인해 Map 인터페이스는 별도로 정의됩니다.
따라서 List 인터페이스와 Set 인터페이스의 공통된 부분을 Collection 인터페이스에서 정의하고 있습니다.
인터페이스설명구현 클래스
List | 순서가 있는 데이터의 집합으로, 데이터의 중복을 허용 | Vector, ArrayList, LinkedList, Stack, Queue |
Set | 순서가 없는 데이터의 집합으로, 데이터의 중복을 허용하지 않음 | HashSet, TreeSet |
Map<K, V> | 키와 값의 한 쌍으로 이루어지는 데이터의 집합으로, 순서가 없음. 이때 키는 중복을 허용하지 않지만, 값은 중복될 수 있음. | HashMap, TreeMap, Hashtable, P roperties |
제네릭이란?
제네릭은 클래스, 인터페이스, 메소드를 정의할 때 타입(Type)을 파라미터로 사용할 수 있게 하는 자바의 기능이다. 제네릭을 사용하면 컴파일 시점에 타입 안정성을 제공하고, 타입 캐스팅을 줄여줌으로써 코드를 더 안전하고 읽기 쉽게 만들 수 있다.
- 타입 안정성: 제네릭을 사용하면 잘못된 타입이 사용되는 것을 컴파일 시점에서 방지할 수 있다. 예를 들어, List<String>에 문자열이 아닌 다른 타입의 객체를 추가하려고 하면 컴파일 오류가 발생한다.
- 타입 캐스팅 감소: 제네릭을 사용하지 않을 경우, 컬렉션에서 객체를 검색하거나 사용할 때 매번 해당 타입으로 캐스팅해야 한다. 제네릭을 사용하면 이러한 불필요한 캐스팅을 줄일 수 있다.
- 코드 재사용성 증가: 제네릭을 사용함으로써 다양한 타입에 대해 동일한 코드를 재사용할 수 있다.
Vector vs ArrayList
동적인 배열을 다루는 컬렉션 프레임워크로서 둘의 차이점을 알아보겠습니다.
- Vector
- 동기화된 상태입니다.(Thread safe)
- 상대적으로 속도가 느립니다.(동기화 되어있기 떄문)
- ArrayList
- 동기화가 안된 상태입니다.
- 상대적으로 속도가 빠릅니다.(동기화가 안되어있기 때문)
- 멀티쓰레드 환경이 아닐 경우 사용 권장합니다.
ArrayList와 LinkedList의 차이
LinkedList와 ArrayList는 모두 Java에서 제공하는 List 인터페이스를 구현한 Collection 구현체입니다.
- ArrayList
- 내부적으로 데이터를 배열로 관리하고 데이터 추가/삭제 시 임시 배열을 생성해 데이터를 복사합니다.
- 데이터별 인덱스가 있어 검색에는 유리
- 임시 배열을 사용하기 때문에 데이터 추가/삭제의 경우에는 불리
- LinkedList
- 내부적으로 노드 단위로 데이터를 관리합니다. 자신의 앞 뒤 노드만 인지하는 상태입니다.
- 인덱스가 따로 없기 때문에 검색 시 전 노드를 순회해야하여 검색시 불리
- 데이터 추가/삭제 시 불필요한 데이터 복사가 없어 유리
오버로딩(Overloading)과 오버라이딩(Overriding)의 차이
- 오버로딩(Overloading) :
오버로딩은 같은 이름의 메서드를 한 클래스 내에 여러 개 정의하는 것을 말한다.오버로딩은 같은 이름의 메서드를 한 클래스 내에 여러 개 정의하는 것을 말한다. 즉, 매개변수의 타입, 개수, 순서가 다르면 같은 이름의 메서드를 여러 개 만들 수 있다. - 오버라이딩(Overriding) :
상위 클래스에 존재하는 메소드를 하위 클래스에서 필요에 맞게 재정의하는 것을 의미합니다.
CheckedException과 UnCheckedException의 차이
CheckedExceptionUnCheckedException
처리 여부 | 반드시 예외를 처리 | 예외 처리를 강제하지 않음 |
확인 시점 | 컴파일 단계 | 실행(Runtime) 단계 |
예외 발생시 트랜잭션 처리 | Roll-Back 하지 않음 | Roll-Back 함 |
예외 종류 | Runtime Exception을 제외한 모든 예외 | Runtime Exception 하위 예외 |
final / finally / finalize() 차이
- final
- final class : 다른 클래스에서 상속하지 못합니다.
- final method : 다른 메소드에서 오버라이딩하지 못합니다.
- final variable : 변하지 않는 상수값이 되어 새로 할당할 수 없는 변수가 됩니다.
- finally : try-catch or try-catch-resource 구문을 사용할 때, 정상적으로 작업을 한 경우와 에러가 발생했을 경우를 포함하여 마무리 해줘야하는 작업이 존재하는 경우에 해당하는 코드를 작성해주는 코드 블록입니다.
- finalize() :finalize() 메서드는 자바에서 객체가 가비지 컬렉션(Garbage Collection)에 의해 회수될 때 자동으로 호출되는 메서드다. java.lang.Object 클래스에 정의되어 있으며, 모든 클래스에서 오버라이드(재정의)할 수 있다. 이 메서드의 목적은 객체가 소멸되기 전에 필요한 정리 작업을 수행하는 것이다. 예를 들어, 열려 있는 파일이나 네트워크 리소스를 닫는 작업 등이 해당될 수 있다.
그러나 finalize() 메서드의 사용은 권장되지 않는다. 그 이유는 다음과 같다:
- 가비지 컬렉션의 예측 불가: finalize()가 호출되는 시점은 JVM의 가비지 컬렉터가 결정하기 때문에, 정확히 언제 실행될지 예측하기 어렵다. 이로 인해 리소스가 필요 이상으로 오래 남아 있을 수 있다.
- 성능 저하: finalize() 메서드의 실행은 가비지 컬렉션 프로세스를 느리게 만들 수 있다. 객체가 finalize()를 오버라이드할 경우, 가비지 컬렉터는 이러한 객체들을 별도로 관리해야 하며, 이는 추가적인 처리 시간을 요구한다.
- 안정성 문제: finalize() 메서드 내에서 발생할 수 있는 예외는 무시되며, 만약 finalize()가 실행 도중 예외로 종료된다면, 나머지 정리 코드는 실행되지 않는다. 또한, finalize() 메서드가 실행 중 객체를 다시 접근 가능한 상태로 만들 경우(Resurrection), 객체의 생명주기를 예측하기 어려워진다.
.net에선 dispose 라는 것을 따로 제공하지만 자바에선 try-with-resource 개념으로 해결한다.
new String()과 “”의 차이
Java에서 문자열은 Heap 영역 내의 String Pool이라는 곳에서 따로 관리하게 됩니다. "" 으로 선언된 String은 String Pool에 추가가 되고 해당 값을 참조 값으로 가지게 됩니다.
반면 new String()으로 생성된 String은 String Pool이 아닌 Heap 영역에 새로운 객체를 등록하게 됩니다.
즉, 위 두 방법으로 객체를 생성하였을 경우 각 객체의 메모리상의 위치가 다른 것입니다.
Spring Security란?
Spring Security는 Spring 기반의 애플리케이션에 보안 기능을 추가하기 위한 강력하고 맞춤화 가능한 인증 및 접근제어 프레임워크입니다. 이 프레임워크는 애플리케이션의 보안 요구 사항을 충족시키기 위해 광범위한 인증, 권한 부여, 그리고 공격으로부터 보호하는 기능을 제공합니다.
주요 기능
- 인증(Authentication): 사용자가 누구인지 확인하는 과정입니다.
- 권한 부여(Authorization): 인증된 사용자가 수행할 수 있는 작업을 결정합니다.
- 공격 방어: CSRF(Cross-Site Request Forgery) 공격 방어, 세션 고정 공격(Session Fixation) 방어, 보안 헤더 추가 등과 같은 보안 취약점을 방어하기 위한 여러 기능을 내장하고 있습니다.
- 세션 관리: 세션 생성 정책, 세션 타임아웃, 동시 세션 제어 등 세션 관리와 관련된 다양한 옵션을 제공합니다.
- 사용자 세부 정보 서비스: 사용자의 정보를 로드하는 방법을 정의합니다.
데이터베이스(DataBase)
JDBC란?
자바에서 데이터베이스에 접속할 수 있도록 하는 자바 API입니다. JDBC는 데이터베이스에서 자료를 쿼리하거나 업데이트하는 방법을 제공합니다.
myBatis란?
JDBC를 좀더 편하게 사용할 수 있도록 객체를 SQL이나 저장 프로시저와 매핑 해주는 프레임워크로 SQL구문을 Java 메소드와 매핑해줍니다.
DCL, DDL, DML 이란?
- DCL(Data Control Language) 데이터 제어어
데이터베이스에 접근하거나 객체에 권한을 주는등의 역할을 하는 언어를 말합니다.(GRANT, REVOKE, COMMIT, ROLLBACK) - DDL(Data Definition Language) 데이터 정의어
데이터베이스를 정의하는 언어이며 데이터를 생성, 수정, 삭제하는 등의 데이터의 전체의 골격을 결정하는 역할을 하는 언어 입니다.(CREATE, ALTER, DROP, TRUNCATE) - DML(Data Manipulation Language) 데이터 조작어
정의된 데이터베이스에 입력된 레코드를 조회하거나 수정하거나 삭제하는 등의 역할을 하는 언어를 말합니다.(SELECT, INSERT, UPDATE, DELETE)
스프링 프레임워크(Spring Framework)
스프링 프레임워크(Spring Framework)란?
자바 앤터프라이즈 개발을 편리하게 해주는 경량급 오픈소스 애플리케이션 프레임워크로 애플리케이션을 개발하는데 필요한 기능들을 포괄적으로 제공하기 때문에 개발자들은 개발에 집중할 수 있습니다.
Spring IoC의 역할
객체의 생성부터 Life Cycle을 관리하고 제어해주는 것을 말합니다. 즉, 제어권이 스프링 프레임워크로 넘어오게 되면서 DI(의존성 주입), AOP(관점 지향 프로그래밍) 등을 가능하게 합니다.
Spring Container의 역할
컨테이너는 보통 객체의 Life Cycle을 관리하며, 생성된 인스턴스에게 추가적인 기능을 제공하도록 하는 것입니다. 다시 말해, 컨테이너란 작성한 코드의 처리과정을 위임받은 독립적인 존재라고 생각하면 됩니다.
컨테이너의 종류로는 BeanFactory, ApplicationContext가 있고 ApplicationContext가 조금 더 많은 기능을 가지고 있습니다.
DI(의존성 주입)란?
의존성 주입(Dependency Injection): 객체 간의 의존성을 컨테이너가 자동으로 처리한다. 개발자는 객체가 필요로 하는 의존 객체를 명시만 해두면, Spring IoC 컨테이너가 자동으로 해당 객체를 찾아서 연결해 준다. 이는 생성자 주입, 세터 메소드 주입, 필드 주입 등의 방법으로 이루어질 수 있다.
즉 객체를 정의 후 생성 하여 사용하는 것이 아니라 방법만 명시해두면 바로 주입해서 사용가능 한 것이다.
Spring과 SpringBoot의 차이
SpringBoot는 Spring Framework에서 사용하는 프로젝트를 간편하게 셋업할 수 있는 서브 프로젝트입니다. 독립 컨테이너에서 동작할 수 있기 때문에 embeded tomcat이 자동으로 실행됩니다.
SpringBoot만의 특징
- 애플리케이션을 신속하게 세팅할 수 있습니다.
- 추가 WAS 설치 없이 embeded tomcat으로 실행합니다.
- 번거로운 개발 세팅을 대신 해줍니다.
POJO란?
POJO(Plain Old Java Object)란 평범한 자바 객체라는 의미입니다. 어떤 자바 객체가 있는데, 이 객체를 사용하기 위해서 상속을 받아야 한다거나, 인터페이스를 구현해야 한다거나, 어노테이션을 적용해야 한다거나 하는 제약조건이 없는 객체라는 뜻입니다.
JPA
JPA란?
JPA는 자바 ORM 기술에 대한 API 표준 명세를 의미합니다. ORM이란 Object Relational Mapping의 약자로 객체와 관계형 데이터베이스의 데이터를 자동으로 매핑(연결)해주는 것을 말합니다. 여기서 객체 지향 프로그래밍은 클래스를 사용하고, 관계형 데이터베이스는 테이블을 사용합니다.
영속성 컨텍스트란?
영속성 컨텍스트는 엔티티를 영구 저장하는 환경입니다. 엔티티 매니저를 생성하면 자동으로 영속성 컨텍스트가 생성되고 엔티티를 관리 혹은 보관할 수 있습니다.
영속성 컨텍스트의 속성
- 비영속 : 영속성 컨텍스트와 전혀 무관한 상태로 순수한 객체의 상태 (처음 객체가 생성되면 비영속 상태)
- 영속 : 영속성 컨텍스트에 저장된 상태
- 준영속 : 영속성 컨텍스트에서 관리하다, 영속성 컨텍스에서 분리된 상태, 준영속 상태는 영속 상태 였던 적이 있기 때문에 @Id 값을 반드시 가지고 있습니다.
- 삭제 : 삭제된 상태
영속성 컨텍스트의 특징
- 1차 캐시
영속성 컨택스트 내부에는 1차 캐시라고 불리는 캐시를 가지고 있습니다. 영속상태의 엔티티는 모두 1차 캐시에 저장되고, 1차 캐시는 @Id를 키로 가지고 있는 Map이 존재합니다. 엔티티를 조회할 때 바로 DB에 접근하는 것이 아니고 1차 캐시에 있는 데이터를 먼저 조회한 후 없는 경우에만 DB에 접근하여 조회 후 다시 1차 캐시에 저장 합니다.
즉, 먼저 DB에 접근하는 것이 아닌 1차 캐시에 먼저 접근함으로서 데이터의 결과를 빠르게 가져올 수 있습니다. - 동일성 보장
1번 특징과 연관되며 모든 엔티티의 데이터들은 1차 캐시에 저장되어지기 때문에
식별자가 동일한 엔티티의 경우 동일성이 보장됩니다. 여기서 동일성이란 같은 객체를 참조한다는 의미입니다. - 트랜잭션을 지연하는 쓰기지연
트랜잭션은 DB에서 하나의 작업 단위를 나타냅니다. 영속성 컨텍스트에서 DML이 발생했을 때 바로 DB에 저장하지 않고, 트랜잭션이 커밋될 때 영속성 컨텍스트의 쓰기지연 SQL 저장소에 모아둔 쿼리들을 한 번에 저장합니다. 이때 쿼리들은 영속성 컨텍스트에 따로 저장이 되며 커밋을 실행하게 되면 flush를 통해 쿼리들을 DB에 저장하게 되고 최종적으로 commit을 하여 DB에 쿼리를 반영합니다.
즉, DB에 접근하는 횟수가 줄어들기 때문에 성능면에서 뛰어납니다. - 변경 감지
영속성 컨텍스트의 1차 캐시에는 스냅샷을 통해 엔티티의 변경을 감지합니다. 변경감지는 오직 영속 상태의 엔티티에만 적용이 됩니다. 순서는 아래와 같습니다.- 트랜잭션을 커밋하면, flush가 호출되고, 엔티티와 스냅샷을 비교해서 변경된 엔티티를 찾습니다.
- 변경된 엔티티가 존재하면, 쿼리를 생성해서 쓰기지연 SQL 저장소에 저장합니다.
- 쓰기지연SQL 저장소에 생성된 쿼리들을 데이터베이스에 flush하고 commit 합니다.
즉시 로딩과 지연 로딩의 차이
- 즉시로딩 : 엔티티를 조회할 때, 연관된 엔티티도 함께 조회한다.
(Question을 조회할 때, List 도 조회) - 지연로딩 : 연관된 엔티티를 실제 사용할 때 조회한다.
(Quesion을 조회할 때, List도 사용한다면 그 때만 조회)
트랜잭션의 의미
트랜잭션(Transaction)은 데이터베이스 관리 시스템(DBMS)에서 안전하게 데이터를 처리하기 위한 일련의 작업 단위를 말합니다. 한 번에 수행되어야 하는 여러 연산을 묶어서 하나의 작업으로 관리하는 것을 의미하며, 이 트랜잭션은 모두 성공하거나 모두 실패해야 합니다.
트랜잭션의 주요 특성 (ACID)
트랜잭션은 다음 네 가지 기본 특성(ACID 속성)을 만족해야 합니다:
- 원자성(Atomicity): 트랜잭션 내의 모든 작업이 완전히 수행되거나 전혀 수행되지 않아야 합니다. 이는 트랜잭션이 중간에 실패하더라도 어떠한 부분 작업도 반영되어서는 안 됨을 의미합니다.
- 일관성(Consistency): 트랜잭션이 성공적으로 완료되면, 데이터베이스는 한 일관된 상태에서 다른 일관된 상태로 변해야 합니다. 트랜잭션 전후로 모든 데이터베이스 규칙을 만족해야 합니다.
- 독립성(Isolation): 여러 트랜잭션이 동시에 실행되는 환경에서, 각 트랜잭션은 다른 트랜잭션의 연산이 끼어들지 않는 독립적인 실행을 보장받아야 합니다. 동시에 실행되는 트랜잭션들은 서로에게 영향을 주지 않아야 합니다.
- 지속성(Durability): 트랜잭션이 성공적으로 완료되면, 그 결과는 영구적으로 데이터베이스에 반영되어야 합니다. 시스템에 문제가 발생해도, 완료된 트랜잭션의 결과는 보존되어야 합니다.
트랜잭션의 중요성
- 데이터 무결성 보장: 트랜잭션을 통해 데이터베이스의 무결성을 유지하면서 데이터를 안전하게 변경할 수 있습니다.
- 복구: 시스템 오류나 장애가 발생했을 때, 트랜잭션 로그를 사용하여 오류 발생 직전 상태로 데이터를 복구할 수 있습니다.
- 동시성 제어: 여러 사용자나 애플리케이션이 데이터베이스에 동시에 접근할 때 발생할 수 있는 문제를 관리하고, 데이터의 일관성을 유지합니다.
Index 설명
인덱스는 데이터베이스에서 데이터 검색 속도를 향상시키는 구조로, 검색 효율성을 높인다. 테이블의 특정 컬럼에 생성되며, 데이터 접근 시간을 단축시키는 책의 목차 같은 역할을 한다. 주로 기본 키나 자주 조회되는 컬럼에 적용되며, B-tree와 해시 인덱스가 일반적이다. 하지만 인덱스는 데이터 변경 작업 시 성능 저하를 일으킬 수 있어, 적절한 사용이 중요하다.
NoSql과 RDB의 차이점
NoSQL과 RDB(관계형 데이터베이스)의 핵심 차이점은 데이터 저장 구조와 확장성에 있다.
- RDB는 엄격한 테이블 구조를 사용하여 데이터를 정규화하고, SQL(Structured Query Language)을 통해 데이터를 관리한다. 데이터의 무결성과 일관성 유지에 강점을 가지며, 복잡한 쿼리와 트랜잭션을 지원한다.
- NoSQL은 비관계형 데이터베이스로, 유연한 데이터 모델을 제공하여 다양한 데이터 유형(문서, 키-값, 그래프 등)을 저장할 수 있다. RDB보다 확장성이 높고, 대량의 분산 데이터를 효율적으로 처리할 수 있다는 장점이 있다. NoSQL은 고정된 스키마가 없어, 빠르게 변화하는 데이터에 적합하다.
NETWORK
HTTP와 HTTPS 프로토콜 차이점
HTTP와 HTTPS의 주요 차이점은 보안에 있다.
- HTTP(HyperText Transfer Protocol)는 웹 서버와 클라이언트 간에 정보를 주고받기 위한 프로토콜이지만, 데이터를 암호화하지 않아 정보가 중간에 가로채질 경우 누구나 내용을 볼 수 있다.
- HTTPS(HyperText Transfer Protocol Secure)는 SSL(Secure Socket Layer) 또는 TLS(Transport Layer Security) 암호화 프로토콜을 사용하여 데이터를 암호화함으로써 보다 안전하게 정보를 전송한다. HTTPS는 데이터의 보안을 보장하여 사용자의 정보를 보호한다.
REST API 란?
쿠키와 세션의 차이점
JWT란?
아래참조
쿠키(Cookie), 세션(Session), 토큰(Token) 의 차이점
해당 내용은 내려갈수록 점점 어려운 내용을 언급하겠다. 처음이 쉬운 사람은 쭉 내려도 무방하다. 일단 쿠키에 대해 알기 앞서 알아야 할 중요 개념들이 있다. 1.브라우저 2.클라이언트와 서버 3
dog-foot-sleep.tistory.com
소중한 공감 감사합니다