컴퓨터

[개발자 면접] 백엔드/자바-스프링 면접 질문과 응답

도도새 도 2024. 1. 23. 17:06
저는 JAVA 백엔드 개발자, 혹은 React 프론트 엔드 개발자를 목표로 하는 취업 준비생입니다. 면접에서 대답할 주요 관심 주제는 Spring, Java, React, Next.js, JavaScript, TypeScript, OS, Network, DB, Docker입니다. 제가 사용해본 기술 중 통합, 정리한 면접 관련 내용을 정리합니다. 틀린 내용이 있다면 댓글로 지적해주시길 바랍니다. 감사합니다

 

자바

 

❓ 자바에 대해 설명해주세요.

더보기

 

자바는 객체지향 프로그래밍 언어이다. 그렇기에 자바에세는 기본 자료형을 제외한 모든 요소가 객체로 표현되며, 객체지향의 특징이 잘 적용된 언어이다. 따라서 객체지향 설계를 통해 유연한 프로그램 설계에 유리하다.

 

객체지향 특징

 - 추상화 

 - 캡슐화

 - 상속

 - 다형성

 

자바는 자바 버츄얼 머신(JVM) 위에서 동작한다. 자바 가상 머신은 CPU가 자바를 인식하고 실행할 수 있게 해주는 가상의 컴퓨터이다. 그렇기에 운영체제에 독립적으로 실행 가능하다. 즉, 리눅스에서도 실행이 가능하며, 윈도우에서도 실행이 가능하다. 또한 가비지 컬렉션을 통해 자동적으로 메모리 관리를 해준다.

 

❓ 가비지 컬렉션에 대해서 설명해주세요.

더보기

 

가비지 컬렉션이란 프로그램에서 더 이상 참조되지 않는 메모리를 식별하고 해제하는 프로세스를 의미한다. 

 

자바에서 객체는 new키워드로 생성되며 힙 메모리에 동적으로 생성된다. 이 객체를 참조 변수를 통해 접근 및 사용하게 된다. 추후 특정 객체에 대한 모든 참조가 사라지면 해당 객체는 가비지 컬렉션의 대상이 된다. 시스템은 주기적으로 가비지 컬렉션을 실행하여 사용하지 않는 객체를 제거하게 된다.

 

사용하지 않는 객체

 - 래퍼런스를 null로 설정(객체 래퍼런스를 담은 변수에 null대입)

 - 래퍼런스에 다른 객체 대입

 - 래퍼런스가 영역을 벗어남.(메서드 내에서 생성된 객체는 해당 메서드가 끝나면 참조가 죽는다.)

 

장점 

 - 프로그램이 알아서 메모리 관리를 해주기 때문에 메모리를 효율적으로 사용 가능하다.

 - 개발자가 메모리 관리를 할 필요가 없기 때문에 한정된 메모리를 효율적으로 사용 가능하다.

 

단점

 - 메모리가 해제되는 시점을 정확히 알 수 없다.

 - GC(가비지 컬렉션)이 실행되는 동안 다른 동작이 멈추게 된다. 즉 가비지 컬렉션 스레드가 아닌 스레드가 동작을 멈추게 된다. 이를 Stop-The-World라 한다.

 

 

❓ 자바의 컴파일 과정을 설명해주세요.

더보기

1. 소스 코드 작성

 - 개발자가 자바 소스코드를 작성한다(확장자는 .java)

 

2. 컴파일

 - 자바 컴파일러(javac)를 이용하여 자바 소스코드를 바이트 코드로 변환한다. (javac Example.java) 결과 확장자는 .class

 

3. 바이트 코드 실행

 - JVM내의 클래스 로더가 .class 파일을 JVM내로 동적으로 로드하낟.

 - 생성된 바이트 코드는 JVM에 의해 실행된다. (java Example)

 - 실행 엔진을 통해 바이트 코드가 컴퓨터가 읽을 수 있는 기계어로 해석된다.

 

❓ 오버로딩과 오버라이딩에 대해 설명해주세요.

더보기

 - 오버 라이딩(Overriding) : 상위 클래스의 메소드를 하위 클래스에서 재정의 하는 것

class Animal {
    public void makeSound() {
        System.out.println("동물이 소리를 낸다.");
    }
}

class Dog extends Animal {
    // Animal 클래스의 makeSound 메서드를 오버라이딩
    @Override
    public void makeSound() {
        System.out.println("개가 짖는다.");
    }
 }

 

 - 오버 로딩(Overloading) : 매개변수 개수, 타입 등을 다르게 하여 같은 이름 메소드를 여러 개 정의하는 것

public class OverloadingExample {

    // 정수형 두 개를 더하는 메서드
    public int add(int a, int b) {
        return a + b;
    }

    // 실수형 두 개를 더하는 메서드 (오버로딩)
    public double add(double a, double b) {
        return a + b;
    }

}

 

❓ 추상 클래스와 인터페이스의 차이점을 말해주세요.

더보기

추상 클래스

 - 하나 이상의 추상 메서드를 포함, 일반 메서드도 가질 수 있다.

 - 상속받는 클래스에서 추상 메서드를 구현하여야한다.

 - 인스턴스를 생성할 수 없다.

 

인터페이스

 - 추상 메서드와 상수만을 가질 수 있다.

 - 상속받는 클래스에서 추상 메서드를 구현하여야한다.

 - 자바 클래스에서 불가능한 다중 상속을 지원한다.

 - 인스턴스를 생성할 수 없다.

 

❓ 객체 지향 설계에 대해 설명해주세요.

더보기

객체 지향 설계(Object-Oriented Design, OOD)는 소프트웨어를 개발할 때 객체 지향 프로그래밍 (OOP) 원칙을 기반으로 하는 설계 접근 방식이다. 이러한 설계는 소프트웨어 시스템을 여러 객체로 나누고, 각 객체가 협력하여 기능을 수행하도록 하는 것을 중요시한다.(https://doompa.tistory.com/418)

 

SOLID원칙 (https://doompa.tistory.com/303)

SRP - 단일 책임 원칙 : 한 클래스는 단 하나의 책임을 가져야한다.,

OCP  - 개방 폐쇄 원칙 : 소프트웨어는 확장에는 열려있어야하고 변경에는 닫혀있어야한다. 즉, 새로운 기능 등을 추가할 대 기존 코드를 수정하지 않고 확장할 수 있게 작성되어야한다.

LSP  - 리스코프 치환 원칙 : 서브 타입은 언제든 기반 타입으로 교체할 수 있어야한다. 즉, 부모 클래스를 상속받은 자식 클래스는 부모 클래스의 메서드를 사용하는 곳에서 문제가 발생하지 않아야한다.

ISP - 인터페이스 분리 원칙 : 인터페이스의 내의 메소드는 최소한 일수록 좋다. 즉, 인터페이스는 필요한 기능만 제공하여야한다.

DIP - 의존 역전 원칙 : 추상화된 인터페이스를 통해 의존성을 주입받아야한다. 또한 고수준 모듈은 저수준 모듈에 의존하여서는 안되며, 추상화에 의존하여야한다.

 

SOLID  장점

 - 코드의 유지보수성을 높이고 확장성을 강화한다.

 - 코드의 재사용성을 높이고 중복을 최소화한다.

 

 

스프링

 

❓ 스프링에서 빈 객체의 생명주기에 대해 설명해주세요.

더보기

빈 객체란 스프링 컨테이너에 의해 관리되는 자바 객체이다. 스프링 컨테이너(Ioc컨테이너)는 빈 객체의 생명주기와 의존성 주입을 관리하게 된다.

 

 1. 빈 객체 싱생

 스프링 컨테이너를 초기화 할 때, @Bean 어노테이션들을 스캔, 혹은 XML 설정 파일 등을 확인하여 빈 객체를 생성하고 등록한다. @Bean을 사용 할 시 @Configuration 어노테이션이 붙은 메서드에 작성한다. 이렇게 할 경우 @Bean 객체가 싱글톤으로 등록됨을 보장한다.

 

2. 의존성 주입

 빈 객체에 필요한 의존성을 주입한다. @Autowired, @Inject, XML설정 등을 통해 작동한다.

 

3. 빈 초기화

  빈 객체를 초기화한다. 

 

4. 빈 사용

 빈 객체를 실제로 사용한다.

 

5. 빈 소멸 준비

 @PreDestroy나 DisposableBean인터페이스를 구현한 destroy 메서드를 이용해 소멸 전 작업을 정의 할 수 있다.

 

6. 빈 소멸

 빈 객체가 스프링 컨테이너에서 제외되고 메모리에서 소멸된다. 스프링 컨테이너가 close()메서드로 종료될 시점(컨테이너가 종료될 시점)에 처리하게 된다.

 

 

❓ 스프링 프레임워크와 스프링 부트의 차이에 대해 설명해주세요.

더보기

 

1 - 스프링 부트는 내장 톰캣 서버가 있어 따로 설치할 필요가 없으며 jar 파일로 간단하게 배포가 가능하다.

2 - 스프링 부트는 특정 목적 달성을 위한 spring boot starter를 이용해 의존성을 직접 관리 할 필요 없다.

 

❓ 스프링에서 DI에 대해 설명해주세요.

더보기

스프링에서 의존성 주입(DI)는 Ioc컨테이너에 의해 관리된다. 객체가 의존하는 객체를 직접 생성하는 대신 외부에 존재하는 객체를 주입받는 것을 의미한다(빈을 주입받는다.)

 

대표적으로 아래 세 가지 방법이 있다. 

 - 생성자 주입

 - 세터 주입

 - 필드 주입

 

이 중 스프링에서는 생성자 주입을 추천한다. 

 

❓ 스프링에서 IoC에 대해 설명해주세요.

더보기

IoC는 제어의 역전을 의미한다. 즉, 시스템 흐름의 제어권을 개발자가 아닌 프레임워크가 관리한다는 것이다. 스프링에서 대표적인 IoC가 바로 DI이다. 객체 간의 의존 관계가 외부에서 주입되며 개발자는 비즈니스 로직에 집중 할 수 있다. 또한 빈의 생성 의존 주입, 생명 주기 관리 등을 IoC컨테이너가 담당하게 된다.

 

❓ JPA에 대해서 설명해주세요

더보기

JPA란 자바 진영에서 사용하는 대표적인 ORM 기술에 대한 표준 명세이다. ORM이란 Object-Relational Mapping으로 데이터베이스와 객체의 상호 작용을 위한 표준 인터페이스를 의미한다. 대표적으로 하이버네이트가 있다.

 

 - JPA를 사용하면 데이터베이스 모델링과 객체 지향 시스템간의 불일치를 해결하고, 객체지향을 통해 데이터 베이스를 조작할 수 있다.

 - Entity : 영속성이 필요한 자바 객체. @Entity로 선언한다. 엔터티 객체의 상태가 변경되면 해당 변경을 DB에 반영하여 영속성을 유지한다.

 - EntityManager : JPA는 EntityManager를 사용해 Entity의 영속성을 관리한다.

 - JPQL : JPQL을 이용하여 엔터티 객체를 대상으로 쿼리를 작성하고 사용할 수 있다.

 - Cascade : 연관된 객체에 특정 작업이 함께 처리되도록 기능을 추가할 수 있다. 예를 들어 유저 엔터티를 삭제할 때 연관된 자식 엔터티인 게시글도 함께 DB에서 삭제 처리 할 수 있다.

 - LazyLoading과 Eager Loading : JPA에서 연관된 엔터티를 어떻게 로딩 할지 설정 가능하다. Lazy Loading은 실제 사용될 때 엔터티를 로딩(예를 들어 User.getPosting()등으로 포스팅 엔터티 호출시 로딩), Eager Loading은 엔터티를 조회할 떼 함께 로딩하는 방식이다.

 

* 영속성 : DB를 생성한 프로그램이 종료되더라도 데이터가 계속해서 저장되어 있는 상태

 

❓ Spring Data JPA에 대해서 설명해주세요.

더보기

Spring Data JPA는 스프링에서 JPA를 쉽게 사용 가능하도록 하는 프로젝트이다. 이는 JPA에 대한 데이터 접근의 추상화이다.(JPA는 명세이기에 구현이 없다.) Spring Data JPA는 내부적으로 Hibernate와 같은 JPA 구현체를 사용하게 된다.

 

 - Repository 인터페이스 : 개발자는 리퍼지토리 인터페이스를 상속하는 것으로 데이터베이스와 관련된 작업을 수행할 수 있다. 기본적으로 CRUD 작업을 지원하며 JPQL등을 활용하여 DB에 접근 또한 가능하다.

 - Auditing 지원 : 엔터티 변경 이력을 추적하기 위한 감시 기능을 제공한다. 예를 들어 특정 엔터티의 생성일을 추적하여 creatAt필드에 삽입 할 수 있다.

 - 다양한 데이터 베이스 지원 : 다양한 데이터 베이스에 대한 접근을 지원한다. 대표적으로 MySql, 그리고 MongoDB, Redis등이 있다.

- 페이징 지원 : Spring Data JPA는 Pegeable 인터페이스를 제공한다. 이를 통해 페이징 및 정렬이 가능하다.

 

❓ @Transactional에 대해 설명해주세요.

더보기

@Transactional은 스프링이 트랜잭션(하나의 작업) 을 처리하기 위한 어노테이션이다. 이 어노테이션을 사용하면 해당 메서드가 하나의 트랜잭션으로 처리되도록 지정 할 수 있다.

 

동작 원리

 - AOP : @Transactional이 붙은 메서드가 호출될 때 스프링은 프록시 기반의 AOP를 사용하여 트랜잭션을 관리한다. AOP를 통해 프록시 객체가 생성되고 해당 프록시 객체는 트랜잭션이 시작하고 종료되는 코드를 적용한다.

 - 커밋과 롤백 : 메서드가 성공적으로 수행되면 트랜잭션은 커밋되고, 그렇지 않을 경우(런타임 에러가 발생할 경우) 롤백된다.

 - 체크드 예외 : 체크드 예외는 RuntimeException을 상속받지 않은 예외 클래스들이다. 체크드 예외에 대해서는 자동 롤백하지 않는다. 단, @Transactional(rollbackFor = Exception.class)과 같이 롤백 조건을 설정 할 수 있다.

 - 전파 : propagation 속성을 통해 트랜잭션 전파를 관리할 수 있다. 예를 들어 REQUIRED로 설정할 시, 이미 진행중인 트랜잭션이 있다면 해당 트랜잭션에 참여한다.

 

❓ @RequestBody, @RequestParam, @ModelAttribute 차이 설명해주세요.

더보기

세 어노테이션은 스프링에서 웹 요청을 처리하는 데 사용되는 어노테이션이다. 각각 웹 요청에서 전달되는 데이터를 어떻게 처리할지를 지정한다.

 

 - RequestBody : HTTP 요청 body의 데이터를 파라미터로 매핑하는 데 사용한다. 주로 POST나 PUT 요청에서 JSON, XML 형식의 데이터를 전송 받아 Java Object로 변환시켜준다. (기본 생성자나 setter 중 하나 필요)

 - RequestParam : HTTP 요청의 파라미터를 매핑하는 데 사용한다. GET요청의 쿼리 파라미터를 전송 받을 때 사용한다.

- ModdelAttribute : HTTP 요청의 파라미터를 객체로 바인딩하는데 사용된다. 주로 폼 데이터 데이터를 받을 때 사용한다. 값을 자바 객체로 변환하는 것이 아닌 주입시킨다.(기본 생성자나 setter 중 하나 필요)

 

❓ DTO, VO, DAO에 대해 살명해주세요.

더보기

 

- DTO : Data Transfer Object의 약자로 계층간(컨트롤러-서비스 등) 데이터 전송 및 교환을 위한 객체이다.

 - VO : Value Object의 약자로 연관된 데이터를 저장하는 객체이다.

 - DAO : Data Access Object의 약자로 데이터베이스에 접근하기 위핸 객체를 의미한다. 대표적으로 Repository 등이 있다.  

상세 설명:

( https://doompa.tistory.com/419)

 

 

❓ 스프링에서 동시성 문제에 대한 해결책을 설명해주세요.

더보기

 

동시성 문제란 여러 프로세스나 스레드가 하나의 자원에 접근하여 발생하는 문제이다.

 

시나리오

예를 들어 thread1과 thread2가 동시에 DB에 접근한다. DB는 hello라는 글자를 반환한다. 그런데 thread1이 hello를 bye로 변환하였다. 그리고 마침내 thread2가 해당 값을 읽는다. hello를 예상했지만, 반환받은 값은 bye가 된다. 

 

해결책

해결책으로는 자원에 하나의 스레드만 도달하도록 보장하면 된다. 이를 위한 방법으로 레디스를 도입할 수 있다. 레디스는 키-값으로 이루어진 싱글 스레드 기반(메인 스레드 1개 서브 스레드 3개)인 메모리 데이터베이스이다. 이러한 레디스트 특성을 이용해 Lock, UnLock을 이용하여 동시에 하나의 스레드만 작업 할 수 있도록 보장하면 된다.

 

 

 

보안 및 스프링 시큐리티

 

❓ 스프링 시큐리티에 대해 설명해주세요.

더보기

 

프링 시큐리티(Spring Security)는 스프링 기반의 어플리케이션에서 보안 관련 기능을 제공하는 프레임워크이다. 사용자 인증, 인가에 대한 다양한 옵션을 제공한다.

 

*스프링 시큐리티는 필터 체인을 통해 구현된다. 그렇기에 디스패처 서블릿 이전에 실행된다. 그러므로 @ExceptionHandler와 같은 어노테이션으로 필터 내의 글로벌 익셉션 캐치가 불가능하다.

 

주요 기능

 -  주요 기능으로 사용자 인증, 권한 부여, 접근 제어, 보안 이벤트 및 리스터, 세션 관리 등이 있다.

구현 코드

(https://doompa.tistory.com/425)

 

❓ CORS가 뭔가요?

더보기

 

CORS란 Cross-Origin Resource Sharing의 약자로 다른 도메인에서 리소스에 접근하는 것을 제어하는 보안 정책이다. 일반적으로 Cors를 허용하지 않으면 다른 도메인에서 스프링 서버로 접근이 제한된다.

 

또한 기존 WebMvcConfigurer에서 cors를 설정하는 것과 다르게 스프링 시큐리티에서 Cors를 설정하면 pre-flight요청에 대해서 허용해주게된다.  (실패 시나리오 : 스프링 시큐리티에서 JWT토큰이 없을 경우 요청을 거부한다고 설정 할 경우 pre-flight요청 또한 거부한다. 이때 cors설정을 시큐리티를 통해 할 시 pre-fight는 통과할 수 있게 된다.) 

 

* pre-fight란 브라우저가 실제 요청을 보내기 전에 보내보는 HTTP요청으로, OPTIONS라는 메서드로 날리게 된다. 이 요청의 응답이 200이어야만 본 요청을 날리게 된다.

 

❓ JWT에 대해 설명해주세요.

더보기

 

JWT(Json Web Token)이란 정보를 안전하게 전송하기 위해 간결한 방식으로 표현하는 JSON 형태의 토큰이다. JWT를 통해 로그인을 구현 할 경우 세션 상태를 서버 측에서 추적 할 필요가 없어서 서버의 비용이 감소한다.

 

JWT는 세 부분으로 이루어져 있다.

헤더 : 헤더에는 토큰의 타입과 서명 알고리즘을 포함한다.

페이로드 : 클레임이라고 불리는 정보의 조각들을 포함한다. 

서명 : 헤더와 페이로드를 인코딩하고 비밀 키를 사용하여 서명한 부분이다. 서버에서 해당 JWT가 유효한지 판단하는 기준이 된다.

 

실제 예시

//헤더
{
  "alg": "HS256"
}

//페이로드
{
  "sub": "userId123",
  "roles": [
    "USER"
  ],
  "exp": 1705997060,
  "iat": 1705997055
}

//시그니처
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  
  your-256-bit-secret

) secret base64 encoded

 

헤더 : HS256 알고리즘 사용을 알린다.

페이로드 : sub는 주체를 나타낸다. 즉 사용자 아이디이다. roles은 역할을 나타낸다. exp는 만료 기한 iat는 생성 기한을 나타낸다.

시그니처 :  HMACSHA256 알고리즘을 사용하여 헤더와 페이로드, "."를 합한 값을 base64로 인코딩하여 시그니처로 사용한다. 서명 생성에는 "your-256-bit-secret"이라는 비밀키를 사용하며, 이 비밀키는 스프링 서버에서 토큰 생성에 사용한 비밀키로, 서버만 그 값을 알고 있으며 노출해서는 안 된다.