1. 가상화가 무엇이고, 이것이 가상머신과 어떠한 차이가 있는지 설명해 주세요.
기본 답변
가상화는 물리 자원을 추상화해 여러 환경이 독립적으로 사용하는 것처럼 보이게 하는 기술입니다.
CPU, 메모리, 스토리지, 네트워크 등을 논리적으로 나눠 사용할 수 있습니다.
가상머신은 가상화 기술로 만들어진 실행 환경 중 하나이며, 하이퍼바이저가 가상 하드웨어를 제공하고 그 위에서 게스트 OS를 실행합니다.
컨테이너는 VM과 다른 격리 방식으로, 호스트 OS 커널을 공유하면서 프로세스를 격리합니다. 일반적으로 VM은 독립된 커널 덕분에 격리 경계가 강한 대신 자원 비용이 크고, 컨테이너는 가볍고 배포가 빠른 대신 공유 커널을 고려해야 합니다.
핵심 키워드
- Virtualization
- Hypervisor
- Virtual Machine
- Container
- Isolation
- Namespace/Cgroup
꼬리질문
질문 그렇다면 Docker는 둘 중 어디에 속하나요? 왜 사람들이 Docker를 많이 채택할까요?
답변 포인트 Docker는 컨테이너 기술입니다.
가볍고 빠르게 실행되며, 이미지 기반으로 환경을 일관되게 배포할 수 있어 많이 사용됩니다.
질문 하나의 Host OS에서 돌아간다면 한 컨테이너가 다른 컨테이너에 간섭할 위험이 있지 않을까요?
답변 포인트 namespace로 프로세스/네트워크/파일시스템을 격리하고 cgroup으로 자원 사용량을 제한합니다.
다만 커널을 공유하므로 권한, 이미지 보안, privileged 실행은 조심해야 합니다.
질문 Docker 위에 Docker를 올릴 순 없을까요?
답변 포인트 가능합니다.
Docker-in-Docker 또는 호스트 Docker socket mount 방식이 있습니다.
CI에서 쓰이지만 보안과 캐시, 권한 문제를 고려해야 합니다.
주의할 점
- 컨테이너는 VM보다 가볍지만 VM과 같은 수준의 완전한 OS 격리는 아닙니다.
2. CI/CD 를 사용해 본 경험이 있나요? 있다면 간단하게 설명해 주세요.
기본 답변
CI는 Continuous Integration으로, 코드 변경이 자주 통합될 때 자동으로 빌드와 테스트를 수행해 문제를 빨리 발견하는 방식입니다.
CD는 Continuous Delivery 또는 Deployment로, 검증된 변경 사항을 배포 가능한 상태로 만들거나 실제 환경에 자동 배포하는 흐름입니다.
실무에서는 Git push나 PR 생성 시 테스트, 정적 분석, 빌드가 실행되고, main 브랜치 병합 후 staging 또는 production 배포 파이프라인이 동작하는 식으로 구성합니다.
핵심 키워드
- CI
- CD
- Pipeline
- Build
- Test
- Deploy
꼬리질문
질문 CI/CD에서 중요한 점은 무엇인가요?
답변 포인트 빠른 피드백, 재현 가능한 빌드, 자동 테스트, 배포 자동화, rollback 전략, secret 관리가 중요합니다.
질문 테스트 없이 배포 자동화만 해도 CD라고 할 수 있나요?
답변 포인트 자동 배포 흐름은 될 수 있지만 검증 없는 자동화는 위험합니다.
CI/CD의 가치는 자동 검증과 안전한 배포에 있습니다.
주의할 점
- CI/CD는 도구 이름이 아니라 개발과 배포를 안정적으로 반복하기 위한 프로세스입니다.
3. static 키워드는 어떤 의미를 갖나요?
기본 답변
static은 인스턴스가 아니라 클래스 자체에 속한다는
의미입니다.
Java에서 static 필드와 메서드는 객체를 생성하지 않고 클래스 이름으로 접근할 수 있고, 모든 인스턴스가 공유합니다.
상수, 유틸리티 메서드, 공유 상태 표현에 사용할 수 있지만, 전역 상태처럼 남용하면 테스트가 어려워지고 동시성 문제가 생길 수 있습니다.
핵심 키워드
- Class Level
- Static Field
- Static Method
- Shared State
- Class Loading
- Static Final
꼬리질문
질문 컴파일 할 때, static 키워드가 붙은 변수, 함수는 어떻게 처리되나요?
답변 포인트 static 멤버는 인스턴스가 아니라 클래스 메타데이터와 함께 관리됩니다.
static 메서드는 바이트코드에서 invokestatic으로
호출됩니다.
질문 Java에서 static과 static final은 어떤 차이를 갖나요? final과 static final은요?
답변 포인트 static은 클래스 소속, final은 재할당 금지입니다.
static final은 클래스 수준 상수에 자주 쓰이고, 컴파일 타임 상수는 인라인될 수 있습니다.
주의할 점
-
static final참조 타입은 참조 재할당이 안 된다는 뜻이지 객체 내부가 항상 불변이라는 뜻은 아닙니다.
4. 객체지향 프로그래밍이 무엇인가요?
기본 답변
객체지향 프로그래밍은 데이터와 그 데이터를 다루는 행위를 객체로 묶고, 객체 간 협력으로 프로그램을 구성하는 방식입니다.
핵심 개념은 캡슐화, 추상화, 상속, 다형성입니다.
좋은 객체지향 설계는 단순히 클래스를 많이 만드는 것이 아니라, 변경에 강하고 책임이 명확한 객체들이 낮은 결합도와 높은 응집도를 갖도록 구성하는 것입니다.
핵심 키워드
- Encapsulation
- Abstraction
- Inheritance
- Polymorphism
- SOLID
- Cohesion/Coupling
꼬리질문
질문 SOLID 원칙에 대해 설명해 주세요.
답변 포인트 단일 책임, 개방-폐쇄, 리스코프 치환, 인터페이스 분리, 의존성 역전 원칙입니다.
변경에 유연하고 테스트하기 쉬운 구조를 만들기 위한 원칙입니다.
질문 다형성이 무엇인지 설명하고, 동적 다형성과 정적 다형성이 무엇인지 설명해 주세요.
답변 포인트 다형성은 같은 메시지에 객체별로 다르게 응답하는 성질입니다.
동적 다형성은 오버라이딩과 런타임 바인딩, 정적 다형성은 오버로딩처럼 컴파일 시점에 결정되는 형태입니다.
질문 오버로딩과 오버라이딩의 차이에 대해 설명해 주세요.
답변 포인트 오버로딩은 같은 이름의 메서드를 매개변수 시그니처를 다르게 정의하는 것이고, 오버라이딩은 상위 타입의 메서드를 하위 타입에서 재정의하는 것입니다.
질문 클래스가 있는 언어는 반드시 객체지향 언어라고 할 수 있을까요? 그 반대는 성립하나요?
답변 포인트 클래스가 있다고 반드시 좋은 객체지향을 하는 것은 아닙니다.
JavaScript처럼 클래스 기반이 아니어도 객체지향적 프로그래밍이 가능합니다.
주의할 점
- 객체지향을 “상속을 많이 쓰는 것”으로 설명하면 좋지 않습니다. 역할, 책임, 협력이 더 중요합니다.
5. 프레임워크와 라이브러리의 차이에 대해 설명해 주세요.
기본 답변
라이브러리는 개발자가 필요할 때 호출해서 사용하는 코드 모음입니다.
제어 흐름은 개발자 코드가 갖고 있습니다.
프레임워크는 애플리케이션의 기본 구조와 실행 흐름을 제공하고, 개발자는 그 안에 필요한 코드를 넣습니다.
핵심 차이는 제어의 역전입니다.
라이브러리는 내가 호출하고, 프레임워크는 프레임워크가 내 코드를 호출합니다.
핵심 키워드
- Library
- Framework
- Inversion of Control
- Control Flow
- Extension Point
- Convention
꼬리질문
질문 Spring은 왜 프레임워크라고 할 수 있나요?
답변 포인트 객체 생성, 의존성 주입, 요청 처리, 트랜잭션, AOP 같은 흐름을 Spring 컨테이너가 관리하고 개발자 코드를 적절한 시점에 호출하기 때문입니다.
주의할 점
- 프레임워크와 라이브러리의 차이는 크기보다 제어 흐름의 주도권입니다.
6. Call By Value와 Call By Reference의 차이를 본인의 언어를 기반으로 설명해 주세요.
기본 답변
Call by value는 함수 호출 시 값이 복사되어 전달되는 방식이고, call by reference는 변수의 참조 자체가 전달되어 함수 안에서 원본 변수에 영향을 줄 수 있는 방식입니다.
Java는 항상 call by value입니다.
객체를 넘길 때도 객체 자체가 아니라 객체를 가리키는 참조값이 복사되어 전달됩니다.
그래서 객체 내부 상태 변경은 보이지만, 매개변수에 새 객체를 대입해도 호출자의 참조가 바뀌지는 않습니다.
핵심 키워드
- Call by Value
- Call by Reference
- Reference Value
- Java
- Mutable Object
- Parameter Passing
꼬리질문
질문 사실 이 질문에는 약간의 낚시가 있습니다. 과연 모든 언어에 저 개념이 존재할까요?
답변 포인트 언어마다 평가 전략이 다릅니다.
call by sharing, call by name 등 다양한 모델이 있고, Java는 객체 참조값을 값으로 전달하는 방식으로 설명하는 것이 정확합니다.
주의할 점
- Java를 call by reference라고 말하면 면접에서 지적받기 쉽습니다.
7. 순수함수가 무엇인지를 함수형 프로그래밍 매커니즘과 연관지어 설명해 주세요.
기본 답변
순수함수는 같은 입력에 항상 같은 출력을 반환하고, 외부 상태를 변경하는 side effect가 없는 함수입니다.
함수형 프로그래밍은 이런 순수함수와 불변성을 바탕으로 예측 가능하고 조합하기 쉬운 코드를 작성하려는 패러다임입니다.
순수함수는 테스트가 쉽고 동시성 환경에서도 안전하게 다루기 좋습니다.
하지만 실제 애플리케이션은 I/O, DB, 네트워크처럼 side effect가 필요하므로 이를 격리하고 관리하는 것이 중요합니다.
핵심 키워드
- Pure Function
- Side Effect
- Immutability
- Higher-order Function
- Referential Transparency
- Functional Programming
꼬리질문
질문 Side Effect가 무엇인가요? 이를 모두 없애는 프로그래밍이 이상적이라고 할 수 있을까요?
답변 포인트 함수 외부 상태 변경, I/O, 로그, DB 저장 등이 side effect입니다.
모두 없앨 수는 없고, 핵심 로직과 side effect를 분리해 관리하는 것이 현실적입니다.
질문 왜 함수형 프로그래밍 매커니즘을 사용한다고 생각하시나요?
답변 포인트 예측 가능성, 테스트 용이성, 병렬 처리 안정성, 조합성 때문입니다.
질문 순수함수는 Thread Safe 한가요? 왜 그럴까요?
답변 포인트 외부 공유 상태를 변경하지 않는다면 thread safe합니다.
같은 입력에 같은 출력만 계산하기 때문입니다.
질문 고차함수에 대해 설명해 주세요.
답변 포인트 함수를 인자로 받거나 함수를 반환하는 함수입니다.
map, filter, reduce가 대표 예시입니다.
주의할 점
- 함수형 프로그래밍은 side effect를 완전히 없애는 것이 아니라 제어 가능한 경계로 밀어내는 데 의미가 있습니다.
8. MVC 패턴이 무엇인가요?
기본 답변
MVC는 Model, View, Controller로 역할을 나누는 아키텍처 패턴입니다.
Model은 데이터와 비즈니스 규칙, View는 사용자에게 보여지는 화면, Controller는 사용자의 입력을 받아 Model과 View를 연결하는 역할을 합니다.
관심사를 분리해 유지보수성을 높이고, 화면과 비즈니스 로직의 결합을 줄이는 것이 목적입니다.
핵심 키워드
- Model
- View
- Controller
- Separation of Concerns
- Spring MVC
- Presentation Layer
꼬리질문
질문 다른 아키텍쳐 패턴은 없나요? MVC랑 비교해서 어떤 차이가 있나요?
답변 포인트 MVP, MVVM, Layered Architecture, Clean Architecture, Hexagonal Architecture 등이 있습니다.
MVC가 화면 흐름 분리에 초점을 둔다면, Clean/Hexagonal은 도메인과 외부 의존성 분리에 더 초점을 둡니다.
주의할 점
- Spring MVC에서 Controller에 비즈니스 로직을 모두 넣으면 MVC의 장점을 살리기 어렵습니다.
9. 디자인 패턴이 무엇인지 설명해주고, 대표적인 디자인 패턴에 대해 설명해 주세요.
기본 답변
디자인 패턴은 자주 반복되는 설계 문제에 대한 검증된 해결 방법입니다.
코드 조각이라기보다 객체 간 역할과 관계를 설명하는 설계 언어에 가깝습니다.
대표적으로 생성 패턴의 Singleton, Factory, Builder, 구조 패턴의 Adapter, Decorator, Proxy, 행위 패턴의 Strategy, Observer, Template Method 등이 있습니다.
핵심 키워드
- Design Pattern
- Singleton
- Factory
- Strategy
- Observer
- Proxy
꼬리질문
질문 Singleton의 장단점에 대해 설명해 주세요.
답변 포인트 하나의 인스턴스를 보장해 공유 자원 관리에 유리하지만, 전역 상태가 되어 테스트와 확장, 동시성에 불리할 수 있습니다.
질문 Singleton이 하나의 객체를 생성한다는 것을 어떻게 보장할 수 있을까요?
답변 포인트 생성자를 private으로 막고 static instance를 제공하거나 enum singleton을 사용할 수 있습니다.
지연 초기화 시에는 동시성 처리가 필요합니다.
주의할 점
- 패턴은 무조건 적용하는 것이 아니라 문제 상황이 있을 때 선택하는 도구입니다.
10. GC에 대해 설명해 주세요.
기본 답변
GC는 Garbage Collection으로, 더 이상 사용되지 않는 객체를 자동으로 찾아 메모리를 회수하는 기능입니다.
개발자가 직접 메모리를 해제하지 않아도 되어 메모리 관리 오류를 줄일 수 있습니다.
GC는 보통 root에서 도달 가능한 객체를 살아 있는 객체로 보고, 도달할 수 없는 객체를 수거 대상으로 판단합니다.
Java에서는 힙 영역의 객체가 주요 관리 대상입니다.
핵심 키워드
- Garbage Collection
- Heap
- GC Root
- Reachability
- Mark and Sweep
- Reference Counting
꼬리질문
질문 본인이 사용하는 언어에서는 GC를 어떻게 구현했나요?
답변 포인트 Java는 JVM이 GC를 수행하며, G1, ZGC, Shenandoah 같은 다양한 수집기가 있습니다.
객체 reachability를 기준으로 수거합니다.
질문 GC의 장단점에 대해 설명해 주세요.
답변 포인트 장점은 메모리 해제 부담 감소와 안정성입니다.
단점은 pause, 튜닝 부담, 메모리 사용량 증가, 수거 시점 예측 어려움입니다.
질문 GC는 어떤 영역에 있는 데이터를 관리하나요?
답변 포인트 주로 heap의 객체를 관리합니다.
스택 프레임은 함수 호출 종료와 함께 자동으로 정리됩니다.
질문 Reference Counting 방식과 순환 참조에 대해 설명해 주세요.
답변 포인트 참조 수가 0이면 즉시 해제하는 방식입니다.
두 객체가 서로 참조하면 외부에서 접근 불가능해도 참조 수가 0이 되지 않는 순환 참조 문제가 생깁니다.
주의할 점
- GC가 있다고 메모리 누수가 사라지는 것은 아닙니다. 불필요한 참조를 계속 들고 있으면 수거되지 않습니다.
11. 32비트와 64비트의 차이는 무엇인가요?
기본 답변
32비트와 64비트는 CPU가 한 번에 처리할 수 있는 레지스터 크기와 주소 표현 능력과 관련이 있습니다.
32비트는 주소를 32비트로 표현하므로 이론적으로 4GB 주소 공간을 가집니다.
64비트는 훨씬 큰 주소 공간을 표현할 수 있습니다.
64비트 환경은 더 큰 메모리를 사용할 수 있고 레지스터와 명령어 셋 차이로 성능상 이점도 있을 수 있지만, 포인터 크기가 커져 메모리 사용량이 늘 수 있습니다.
핵심 키워드
- 32-bit
- 64-bit
- Address Space
- Pointer Size
- Register
- 4GB
꼬리질문
질문 32비트에서 가용한 메모리의 크기는 최대 4GB라고 하는데, 왜 그런걸까요?
답변 포인트 32비트 주소는
2^32개의 주소를 표현할 수 있고, 바이트 단위 주소라면 약
4GB입니다.
다만 실제 사용자 프로세스가 모두 쓸 수 있는지는 OS 정책에 따라 다릅니다.
주의할 점
- 가상 주소 공간 한계와 실제 물리 RAM 사용 한계를 구분해야 합니다.
12. 인증과 인가의 차이에 대해 설명해 주세요.
기본 답변
인증은 사용자가 누구인지 확인하는 과정이고, 인가는 인증된 사용자가 특정 자원이나 기능에 접근할 권한이 있는지 확인하는 과정입니다.
예를 들어 로그인은 인증이고, 관리자 페이지 접근 가능 여부를 판단하는 것은 인가입니다.
보안 설계에서는 두 단계를 명확히 분리해야 합니다.
핵심 키워드
- Authentication
- Authorization
- Principal
- Role
- Permission
- OAuth
꼬리질문
질문 OAuth가 무엇인지 설명하고, 이것은 인증인지 인가인지에 대해 설명해 주세요.
답변 포인트 OAuth 2.0은 기본적으로 인가 프레임워크입니다.
사용자가 제3자 애플리케이션에 자원 접근 권한을 위임할 수 있게 합니다.
인증 용도로는 OpenID Connect를 함께 사용합니다.
주의할 점
- OAuth를 로그인 프로토콜이라고만 말하면 부족합니다. 인증은 OIDC까지 함께 설명하는 것이 정확합니다.
13. JWT 형식과 인증/인가에서의 활용에 대해 설명해 주세요.
기본 답변
JWT는 당사자 사이에 claim을 전달하기 위한 JSON 기반 토큰 형식입니다. 인증 방식 그 자체라기보다 인증·인가 시스템에서 access token 등의 표현 형식으로 사용할 수 있습니다.
서명된 compact JWS 형태는 보통 header.payload.signature 세
부분으로 구성되며 서명으로 위변조 여부를 확인합니다. JWT는 암호화된
JWE나 중첩 형태로도 표현될 수 있습니다.
JWT를 사용한다고 시스템이 자동으로 stateless해지는 것은 아닙니다. 서버 저장소 없이 서명과 claim만 검증하도록 설계할 수 있지만, 폐기 목록·세션·refresh token을 관리하면 서버 상태가 생깁니다.
발급 후 만료 전까지 강제 폐기가 어려울 수 있고 탈취 시 위험하므로 만료 시간, key 관리, refresh token, 저장 위치와 폐기 정책을 신중히 설계해야 합니다.
핵심 키워드
- JWT
- Header
- Payload
- Signature
- Access Token
- Refresh Token
꼬리질문
질문 Signature는 어떻게 만들어지나요?
답변 포인트 서명형 compact JWS에서는 Base64URL로 인코딩한 protected header와 payload를 점으로 연결한 signing input에 비밀키 MAC 또는 개인키 서명을 적용합니다.
토큰 header가 주장하는 알고리즘을 그대로 신뢰하지 말고 서버가 사전에 허용한 알고리즘, issuer, audience와 올바른 key를 함께 검증해야 합니다.
질문 Access Token이 탈취되면 어떻게 대응할 수 있을까요?
답변 포인트 짧은 만료 시간, HTTPS, 안전한 저장소, token blacklist, 재발급 정책, 의심 세션 종료 등을 사용합니다.
질문 Refresh Token이 탈취되면 어떻게 대응해야 할까요?
답변 포인트 refresh token rotation, 재사용 감지, 서버 저장소 관리, 즉시 폐기, 전체 세션 로그아웃이 필요합니다.
access token보다 더 민감하게 다뤄야 합니다.
주의할 점
- 일반적인 서명형 JWT payload는 Base64URL 인코딩됐을 뿐 암호화된 것이 아닙니다. 민감 정보를 넣지 않아야 하며 기밀성이 필요하면 JWE 같은 암호화 형식을 사용해야 합니다.
- 서명이 유효하다는 사실만으로 현재 사용자 상태, 권한, 토큰 폐기 여부까지 자동으로 보장되지는 않습니다.
14. 암호화 알고리즘에 대해 설명해 주세요.
기본 답변
암호화는 데이터를 권한 없는 사람이 읽을 수 없도록 변환하는 기술입니다.
크게 대칭키 암호화와 비대칭키 암호화로 나눌 수 있습니다.
대칭키는 같은 키로 암호화와 복호화를 수행해 빠르고, 비대칭키는 공개키와 개인키 쌍을 사용해 키 교환이나 전자서명에 유리합니다.
비밀번호 저장에는 복호화 가능한 암호화가 아니라 해시와 salt, 느린 KDF를 사용해야 합니다.
핵심 키워드
- Symmetric Encryption
- Asymmetric Encryption
- Hash
- Salt
- KDF
- TLS
꼬리질문
질문 암호화와 해시는 어떤 차이가 있나요?
답변 포인트 암호화는 키로 복호화가 가능한 변환이고, 해시는 원칙적으로 되돌릴 수 없는 단방향 함수입니다.
질문 비밀번호는 어떻게 저장해야 하나요?
답변 포인트 평문이나 일반 암호화가 아니라 bcrypt, scrypt, Argon2 같은 password hashing 알고리즘과 salt를 사용해야 합니다.
주의할 점
- 직접 암호 알고리즘을 만들거나 검증되지 않은 방식으로 조합하면 위험합니다.
15. 문자열 인코딩에 대해 설명해 주세요.
기본 답변
문자열 인코딩은 문자를 컴퓨터가 저장하고 전송할 수 있는 바이트로 변환하는 규칙입니다.
ASCII, EUC-KR, UTF-8, UTF-16 등이 대표적입니다.
UTF-8은 유니코드 문자를 가변 길이 바이트로 표현하며, ASCII와 호환되고 웹에서 널리 사용됩니다.
인코딩이 맞지 않으면 한글 깨짐 같은 문제가 발생합니다.
핵심 키워드
- Character Set
- Encoding
- Unicode
- UTF-8
- ASCII
- Base64
꼬리질문
질문 Base64 인코딩은 일반적인 문자열 인코딩과는 달리 사용자가 읽기 어려운 알파벳과 숫자 조합으로 변경합니다. 이를 사용하는 이유는 무엇일까요?
답변 포인트 바이너리 데이터를 텍스트만 안전하게 전달할 수 있는 환경에서 전송하기 위해 사용합니다.
암호화가 아니며 원본으로 쉽게 디코딩할 수 있습니다.
주의할 점
- Base64는 보안 목적의 암호화가 아닙니다.
16. Git에 대해 설명해 주세요.
기본 답변
Git은 분산 버전 관리 시스템입니다.
파일의 변경 이력을 commit 단위로 저장하고, branch를 통해 여러 작업 흐름을 독립적으로 진행할 수 있습니다.
중앙 서버 없이도 로컬 저장소에 전체 이력을 가지고 작업할 수 있으며, 원격 저장소와 push/pull/fetch로 동기화합니다.
핵심 키워드
- Repository
- Commit
- Branch
- Merge
- Rebase
- Remote
꼬리질문
질문 여러 브랜치를 합쳐야 할 때, 어떤 방법을 사용할 수 있는지 모두 설명해 주세요.
답변 포인트 merge는 이력을 보존하며 브랜치를 합치고, rebase는 기준 브랜치 위로 커밋을 재배치해 선형 이력을 만듭니다.
squash merge는 여러 커밋을 하나로 합쳐 병합합니다.
cherry-pick은 특정 커밋만 골라 적용합니다.
주의할 점
- 이미 공유된 브랜치를 함부로 rebase하고 force push하면 협업자 이력을 망가뜨릴 수 있습니다.
17. 단위 테스트, 통합 테스트, E2E 테스트의 차이와 테스트 전략을 설명해 주세요.
기본 답변
단위 테스트는 함수나 클래스처럼 작은 테스트 경계를 빠르고 독립적으로 검증합니다. 통합 테스트는 데이터베이스, 메시지 브로커, 프레임워크 설정처럼 여러 구성 요소의 실제 협력을 확인하고, E2E 테스트는 사용자 요청부터 최종 응답까지 배포된 시스템의 전체 흐름을 검증합니다.
단위 테스트의 “단위”가 반드시 메서드 하나를 뜻하지는 않습니다. 외부에서 관찰할 수 있는 행위와 계약을 경계로 잡고, 느리거나 제어하기 어려운 의존성만 테스트 더블로 대체해야 리팩터링에 강한 테스트가 됩니다. 반대로 SQL, 트랜잭션, 직렬화처럼 실제 기술의 동작이 중요한 경계는 통합 테스트로 확인해야 합니다.
테스트 피라미드는 빠른 단위 테스트를 넓게 두고 비용이 큰 통합·E2E 테스트를 핵심 경로에 집중하라는 경험칙입니다. 고정된 비율보다 장애 위험, 변경 빈도와 외부 의존성의 중요도를 기준으로 조절하며, 서비스 사이의 요청·응답 스키마나 이벤트 계약은 계약 테스트로 각 서비스를 모두 실행하지 않고도 호환성을 검증할 수 있습니다.
좋은 자동 테스트는 같은 입력에서 반복 가능한 결과를 내고 서로의 실행 순서나 공유 상태에 의존하지 않아야 합니다. 시간, 난수, 네트워크, 공유 DB와 비동기 타이밍을 제어하고 테스트별 데이터를 격리해야 병렬 실행과 실패 재현이 쉬워집니다. 다만 실제 환경과 지나치게 동떨어진 가짜 구현만 사용하면 잘못된 확신을 줄 수 있으므로 속도와 현실성 사이의 균형이 필요합니다.
핵심 키워드
- Unit Test
- Integration Test
- E2E Test
- Test Pyramid
- Test Double
- Determinism
- Test Isolation
- Contract Test
꼬리질문
질문 Mock, Stub, Fake는 어떻게 다른가요?
답변 포인트 Stub은 준비한 응답을 반환해 테스트 상황을 만들고, Mock은 특정 호출이나 인자, 횟수 같은 상호작용이 기대대로 발생했는지 검증합니다. Fake는 인메모리 저장소처럼 실제 동작하지만 운영용 구현보다 단순한 구현체입니다.
모든 의존성을 Mock으로 바꾸면 구현 호출 순서에 테스트가 결합될 수 있습니다. 상태나 반환 결과로 충분히 검증할 수 있다면 상태 검증을 우선하고, 외부로 반드시 전달해야 하는 명령처럼 상호작용 자체가 계약인 경우에 Mock을 이용한 행위 검증을 사용하는 것이 좋습니다.
질문 리팩터링할 때마다 테스트가 깨진다면 무엇이 문제일까요?
답변 포인트 외부 계약보다 private 메서드, 내부 자료구조와 호출 순서 같은 구현 세부사항을 지나치게 검증했을 가능성이 큽니다.
같은 입력에 대한 결과, 상태 변화, 발생해야 하는 도메인 이벤트처럼 관찰 가능한 행위를 검증하고, 테스트 데이터 생성과 공통 준비 코드를 단순화하면 리팩터링 내성이 높아집니다.
질문 간헐적으로만 실패하는 flaky test는 어떻게 원인을 찾고 줄일 수 있나요?
답변 포인트 시스템 시각, 난수 seed, 비동기 완료 순서, 공유 파일·포트·DB 데이터, 외부 네트워크처럼 비결정적인 입력을 먼저 확인합니다. 고정 시계와 seed를 주입하고, 테스트별 자원을 격리하며, 임의의 sleep 대신 명시적인 완료 조건을 기다려야 합니다.
무조건 재시도해 녹색으로 만드는 방식은 원인을 숨길 수 있습니다. 실패 당시 입력, 스레드와 외부 의존성 상태를 재현할 수 있도록 기록하고 격리 실패인지 제품 결함인지 구분해야 합니다.
질문 계약 테스트는 통합 테스트나 E2E 테스트와 무엇이 다른가요?
답변 포인트 계약 테스트는 서비스 제공자와 소비자가 합의한 요청·응답, 이벤트 스키마와 의미가 서로 호환되는지 검증합니다. 전체 시스템을 함께 실행하는 E2E보다 빠르고 실패 원인을 좁히기 쉽습니다.
다만 실제 네트워크 설정, 인증, 데이터베이스와 배포 환경의 연결까지 보장하지는 않으므로 중요한 경계의 통합 테스트와 소수의 핵심 E2E 테스트를 완전히 대체하지는 않습니다.
주의할 점
- 테스트 개수나 커버리지만으로 결함 탐지 능력을 판단하면 안 됩니다.
- DB를 Mock으로 대체한 테스트만으로 실제 쿼리와 트랜잭션을 검증할 수는 없습니다.
- 테스트 사이에 실행 순서 의존성이나 공유 상태가 생기면 개별 실행은 통과해도 전체·병렬 실행에서 실패할 수 있습니다.
18. 직렬화와 역직렬화가 무엇이며, 데이터 형식을 선택할 때 무엇을 고려해야 하나요?
기본 답변
직렬화는 메모리의 데이터나 객체 상태를 저장·전송 가능한 텍스트 또는 바이트 표현으로 바꾸는 과정이고, 역직렬화는 그 표현을 프로그램이 사용할 데이터로 복원하는 과정입니다. HTTP 메시지, 이벤트, 캐시와 파일처럼 프로세스나 시간의 경계를 넘을 때 사용하므로 메모리 객체 구조와 전송 형식을 같은 것으로 보면 안 됩니다.
JSON은 사람이 읽기 쉽고 다양한 환경에서 지원되지만 필드 이름이 반복되어 크기가 커지고 숫자 타입이나 날짜 표현을 별도로 합의해야 할 수 있습니다. Protocol Buffers 같은 스키마 기반 이진 형식은 보통 더 작은 payload와 빠른 처리를 기대할 수 있지만 스키마와 생성 코드 관리가 필요합니다. 실제 성능은 데이터 형태, 압축, 파서 구현과 네트워크 비용에 따라 달라지므로 대표 데이터로 크기와 CPU 사용량을 측정해야 합니다.
스키마를 진화시킬 때는 기존 필드의 이름·타입·의미를 함부로 바꾸지 않고 새 필드는 선택적으로 추가하는 것이 안전합니다. 필드 번호를 사용하는 이진 형식에서는 기존 번호를 재사용하지 말고 제거한 번호를 예약해야 하며, 소비자는 알 수 없는 필드를 허용하고 누락된 필드의 기본 동작을 정의해야 합니다. 새 작성자의 데이터를 옛 독자가 읽는 forward compatibility와 옛 데이터를 새 독자가 읽는 backward compatibility를 각각 확인해야 합니다.
역직렬화 입력은 신뢰 경계로 취급해야 합니다. 허용할 타입과 필드를 제한하고 payload 크기, 중첩 깊이, 컬렉션 길이와 처리 시간을 제한하며 복원된 값에 업무 규칙 검증을 다시 적용해야 합니다. 특히 임의 타입을 생성하거나 실행 훅을 호출하는 네이티브 객체 역직렬화는 신뢰할 수 없는 데이터에 사용하지 않는 것이 원칙입니다.
핵심 키워드
- Serialization
- Deserialization
- JSON
- Binary Format
- Wire Format
- Schema Evolution
- Backward Compatibility
- Forward Compatibility
꼬리질문
질문 JSON과 Protocol Buffers 중 어느 것이 더 좋은가요?
답변 포인트 절대적인 우열은 없습니다. 브라우저와 외부 공개 API처럼 범용성, 가독성과 디버깅 편의가 중요하면 JSON이 자연스럽고, 내부 대량 통신에서 명시적 스키마, payload 크기와 처리 비용이 중요하면 Protocol Buffers가 유리할 수 있습니다.
압축된 JSON이 충분히 작거나 변환 비용이 전체 지연의 작은 부분일 수도 있습니다. 예상 트래픽과 실제 데이터로 측정하고 스키마 배포, 도구와 운영자의 디버깅 비용까지 포함해 선택해야 합니다.
질문 이전 소비자와의 호환성을 유지하려면 어떻게 하나요?
답변 포인트 기존 필드의 이름·타입·의미와 이진 형식의 필드 번호를 유지하고, 새 필드는 기존 소비자가 무시할 수 있는 선택 필드로 추가합니다. 필드를 제거할 때는 번호나 이름을 다른 의미로 재사용하지 않고 충분한 이전 기간을 둡니다.
알 수 없는 enum 값과 필드를 만났을 때의 동작, 필드가 없을 때의 기본값도 계약에 포함해야 합니다. 생산자와 소비자의 이전·현재 버전 조합을 계약 테스트나 호환성 테스트로 확인하는 것이 좋습니다.
질문 backward compatibility와 forward compatibility는 어떻게 다른가요?
답변 포인트 Backward compatibility는 새 코드가 과거 형식의 데이터를 읽을 수 있는 성질이고, forward compatibility는 과거 코드가 새 형식의 데이터를 가능한 범위에서 처리할 수 있는 성질입니다.
양쪽을 모두 지원하려면 새 필드를 선택적으로 추가하고 알 수 없는 필드를 허용하는 tolerant reader가 필요합니다. 다만 모르는 필드를 읽고 다시 저장하는 중간 서비스가 그 필드를 유실할 수 있는지도 확인해야 합니다.
질문 신뢰할 수 없는 역직렬화 입력은 어떻게 방어하나요?
답변 포인트 임의 클래스 생성이 가능한 역직렬화 기능을 피하고 허용 타입·필드 목록을 제한합니다. 입력 전체 크기뿐 아니라 중첩 깊이, 문자열과 배열 길이, 참조 개수, 처리 시간에도 상한을 둬 메모리와 CPU 고갈을 막아야 합니다.
파싱 성공은 업무적으로 유효하다는 뜻이 아니므로 범위, 형식, 권한과 교차 필드 규칙을 별도로 검증합니다. 파서와 라이브러리 보안 업데이트도 지속적으로 적용해야 합니다.
주의할 점
- 직렬화는 암호화가 아닙니다.
- 신뢰할 수 없는 입력의 임의 객체 역직렬화는 코드 실행이나 자원 고갈 취약점을 만들 수 있습니다.
- “이진 형식이 항상 더 빠르다”는 가정 대신 실제 payload와 운영 환경에서 측정해야 합니다.
19. API의 하위 호환성과 버전 관리 전략에 대해 설명해 주세요.
기본 답변
API 계약에는 URL과 HTTP 메서드뿐 아니라 요청·응답 필드의 이름, 타입과 필수 여부, 상태 코드, 오류 형식, 정렬·페이지네이션 규칙과 데이터의 의미도 포함됩니다. 서버가 변경된 뒤에도 기존 클라이언트가 수정 없이 의도대로 동작해야 하위 호환성이 유지됩니다.
선택 필드 추가는 일반적으로 호환 가능하지만 엄격한 역직렬화기나 전체 필드 일치를 가정한 소비자는 깨질 수 있습니다. 새 enum 값, nullable 변경, 기본값이나 단위 변경도 문법상 타입이 같아 보여도 기존 분기와 업무 의미를 깨뜨릴 수 있습니다. 삭제, 이름·타입 변경과 의미 재사용처럼 계약을 깨는 변경은 별도 버전이나 단계적 이전이 필요합니다.
버전은 /v2 같은 URI, 전용 헤더, 또는 Accept와
vendor media type을 이용한 content negotiation으로 표현할 수 있습니다.
URI 방식은 눈에 잘 보이고 라우팅·캐시가 단순하지만 리소스 URL이 버전에
묶이고, 헤더·미디어 타입 방식은 URL을 유지할 수 있지만 브라우저 확인과
운영 도구 설정이 복잡해질 수 있습니다. 팀과 소비자 환경에 맞는 한 가지
정책을 일관되게 적용해야 합니다.
Breaking change를 배포할 때는 새 버전을 병행하고 구버전 사용량과 소비자별 이전 상태를 관찰해야 합니다. 폐기 예정과 종료 시점을 문서·응답 헤더 등으로 알리고, 계약 테스트와 실제 트래픽 분석으로 예상하지 못한 소비자를 확인하며, 충분한 유예 기간과 롤백 경로를 둔 뒤 구버전을 종료합니다.
핵심 키워드
- API Contract
- Backward Compatibility
- Breaking Change
- Versioning
- Deprecation
- Contract Test
- Content Negotiation
- Sunset Policy
꼬리질문
질문 응답 필드 추가는 항상 하위 호환 변경인가요?
답변 포인트 대체로 호환되지만 항상 그렇지는 않습니다. 알 수 없는 필드를 거부하는 역직렬화기, 응답 전체를 서명하거나 필드 집합을 검증하는 코드, 전체 필드 일치를 가정한 테스트는 깨질 수 있습니다.
OpenAPI 같은 스키마와 소비자 계약 테스트로 허용 여부를 확인하고, 소비자가 unknown field를 무시하도록 설계하는 것이 좋습니다.
질문 운영 중인 API에 breaking change가 필요하면 어떻게 배포하나요?
답변 포인트 새 버전을 병행 배포하고 폐기 일정, 변경점과 이전 방법을 먼저 공개합니다. 로그와 메트릭으로 소비자별 구버전 사용량을 확인하고 주요 소비자와 이전 일정을 조율합니다.
트래픽을 점진적으로 새 버전에 보내 오류율을 관찰하고 즉시 되돌릴 경로를 유지합니다. 사용자가 남아 있는 구버전을 날짜만 보고 일괄 종료하지 않도록 예외와 연장 정책도 정해야 합니다.
질문 URI 버전과 헤더·미디어 타입 버전은 어떤 차이가 있나요?
답변 포인트 URI 버전은 로그, 문서, 라우팅과 캐시 키에서 명확하게 구분되지만 같은 리소스가 여러 URL로 표현됩니다. 헤더나 vendor media type은 URL을 안정적으로 유지하고 표현 버전을 협상할 수 있지만 사람이 직접 호출하기 어렵고 프록시·캐시가 해당 헤더를 올바르게 반영해야 합니다.
어떤 방식이든 버전 경계와 지원 기간을 일관되게 정의하는 것이 방식 자체보다 중요합니다.
질문 새 enum 값이나 nullable 변경도 breaking change가 될 수 있나요?
답변 포인트 가능합니다. 클라이언트가 모든 enum 값을 열거하고 기본 분기를 두지 않았다면 새 값에서 실패할 수 있고, 필수 값이 null이 되면 역직렬화나 후속 계산이 깨질 수 있습니다.
필드가 없음, 명시적 null, 빈 값과 기본값의 의미를 계약에 구분하고 unknown enum 처리 정책을 제공해야 합니다. 단위나 정렬 기준처럼 타입은 같지만 의미가 바뀌는 변경도 새 필드나 버전으로 이전하는 것이 안전합니다.
주의할 점
- URL 버전만 올린다고 데이터 의미의 호환 문제가 자동으로 해결되지는 않습니다.
- 너무 많은 버전을 장기간 유지하면 보안과 운영 비용이 커집니다.
- 서버 관점의 스키마 비교만으로 호환성을 단정하지 말고 실제 소비자의 역직렬화와 업무 분기를 확인해야 합니다.