본문 바로가기

💻 개발IT/기타

[면접] Spring 기술 면접 질문 정리

 

Java의 특징

  • 객체지향 프로그래밍 언어
  • 장점
    • 운영체제에 독립적 (JVM 위에서 동작해서)
    • 자동적인 메모리 관리 가능 (GabageCollector)
  • 단점
    • 실행 속도가 상대적으로 느림 (JVM 위에서 동작해서)

 

Spring Framework(스프링 프레임워크)

자바 플랫폼을 위한 오픈소스(Open Source) 애플리케이션 프레임워크(Framework)

실행 구조 : https://sunghee2.tistory.com/entry/Spring스프링-실행-구조-Controller-Service-Mapper-DAO-VO

 

Spring(스프링) 실행 구조 (Controller, Service, Mapper, DAO, VO)

Spring 구조 클라이언트가 Controller를 호출하면 Controller → ServiceImpl → DAO (or Mapper) → SqlMapper 를 통해 DB에 접근하게 된다. 이 과정 중에 사용되는 데이터 형식은 DTO / VO로 정의된다. 파일 구조를 보

sunghee2.tistory.com

 

 

메이븐(Maven)와 Gradle

  • 메이븐
    • Apache사에서 만든 빌드 및 종속성 관리 툴
    • pom.xml 파일을 통해 중앙 저장소에서 필요한 라이브러리와 플러그인을 다운로드하여 사용할 수 있도록 하며, 프로젝트의 빌드 과정을 표준화
  • Gradle
    • 그루비(Groovy)를 기반으로 한 빌드 및 종속성 관리 툴

 

Mybatis

자바 객체와 SQL 문장 사이의 자동 매핑 기능을 제공

좀 더 직관적이고 간결하게 데이터베이스 코드를 작성할 수 있도록 도움.

Mybatis는 설정을 통해 SQL 문장을 외부에서 관리하고 최적화할 수 있는 유연성을 제공

 

 

절차지향 프로그래밍와 객체지향 프로그래밍

  • 절차지향 프로그래밍
    • 기능을 순차적으로 처리 ex. C언어
    • 컴퓨터의 처리구조와 유사해 실행속도가 빠름
    • 코드의 순서가 바뀌면 동일한 결과를 보장하기 어려움
  • 객체지향 프로그래밍
    • 실제 세계의 사물들을 객체로 모델링하여 개발. 각각의 객체는 메시지를 주고 받고 데이터를 처리함. ex. Java
    • 코드 재사용성이 높고, 코드 변경이 용이
    • 절차지향 언어보다 실행속도가 느림

 

객체지향의 설계원칙

  1. SRP - 단일 책임 원칙 : 한 클래스는 하나의 책임만 가져야 한다.
  2. OCP - 개방-폐쇄 원칙 : 확장에는 열려있고, 수정에는 닫혀있어야 한다.
  3. LSP - 리스코프 치환 원칙 : 하위 타입은 항상 상위 타입을 대체 할 수 있어야 한다.
  4. ISP - 인터페이스 분리 원칙 : 인터페이스 내에 메소드는 최소한 일수록 좋다. (하나의 일반적인 인터페이스보다 여러 개의 구체적인 인터페이스가 낫다.) SRP와 같은 문제에 대한 두 가지 다른 해결책이다.
  5. DIP - 의존관계 역전 원칙 : 구체적인 클래스보다 상위 클래스, 인터페이스, 추상클래스와 같이 변하지 않을 가능성이 높은 클래스와 관계를 맺어라. DIP 원칙을 따르는 가장 인기 있는 방법은 의존성 주입(DI)이다.

 

Java의 컴파일 과정

개발자가 .java 파일 생성 → 실행하면 JVM은 OS로부터 메모리 할당   java compiler의 javac 명령어 통해 바이트코드(.class) 생성  Class Loader 통해 JVM 메모리 내로 로드  실행엔진을 통해 각 OS에 맞는 기계어로 해석

 

 

Java의 메모리 영역

자바의 메모리 공간은 크게 Method 영역, Stack 영역, Heap 영역으로 구분되고, 데이터 타입에 따라 할당

  • 메소드(Method) 영역 : 전역변수, static변수 저장. 프로그램의 시작부터 종료까지 메모리에 남아있음
  • 스택(Stack) 영역 : 지역변수, 매개변수 저장. 메소드가 호출될 때 메모리에 할당되고 종료되면 해제.
    LIFO(Last In First Out) 구조를 갖고 변수에 새로운 데이터가 할당되면 이전 데이터는 지워짐
  • 힙(Heap) 영역 : new 키워드로 생성되는 객체(인스턴스), 배열 등 저장. 가비지 컬렉션에 의해 메모리가 관리됨

 

각 메모리 영역이 할당되는 시점

  • Method 영역 : JVM이 동작해서 클래스가 로딩될 때 생성
  • Stack 영역 : 메소드가 호출될 때 할당
  • Heap 영역 : 런타임시 할당

 

클래스 멤버 변수 초기화 순서

  1. static 변수 선언부 : 클래스가 로드 될 때 변수가 제일 먼저 초기화
  2. 필드 변수 선언부 : 객체가 생성될 때 생성자 block 보다 앞서 초기화 됨
  3. 생성자 block : 객체가 생성될 때 JVM이 내부적으로 locking( thread-safe 영역 )

 

JVM(Java Virtual Machine)

자바 애플리케이션을 실행하기 위한 환경을 제공, Java Byte Code를 OS에 맞게 해석 

자바 코드를 컴파일하여 생성된 byte code를 각 OS가 이해할 수 있는 기계어로 변환하고 실행

 

 

Garbage Collection(가비지 컬렉션)

프로그램이 동적으로 할당했던 메모리 영역 중에서 더 이상 사용되지 않는 영역을 자동으로 탐지하고, 회수하여 메모리 사용 효율 최적화

미사용 객체를 판단하기 위해 도달 가능성(Reachability) 분석을 사용하며, 이는 객체에 대한 참조가 존재하는지 여부를 검사

  • 미사용 객체 판단 방법 
    • 객체에 레퍼런스가 있으면 Reachable(참조된 상태)로 구분하고, 없으면 Unreachable(참조되지 않은 상태)로 구분하여 제거

 

  • 청소 방식 (Mark and Sweep)
    • Mark : Root Space부터 그래프 순회를 통해 연결된 객체를 찾아서 Reachable 마킹함
    • Sweep : Unreachable 객체를 Heap에서 제거
더보기

Root Space : Heap 메모리 영역을 참조하는 method area, static 변수, stack 등이 있음

  • 동작 과정
    • 객체는 대부분 일회성이며, 메모리에 오래 남아있는 경우는 드묾
    • Young 영역
      • 새롭게 객체가 할당 되는 영역
      • Eden
        • 처음 생성된 객체 추가
        • Eden 영역이 꽉 차면 Reachable 객체를 Survivor 영역으로 이동시키고 UnReachable 객체는 해제(sweep)
      • Survivor 0 / Survivor 1
        • 최소 1번의 GC 이상 살아남은 객체가 존재하는 영역
        • 둘 중 꼭 하나는 비어있어야 함
          • Eden이 꽉 찼을 때 Survivor 0 (1)에서도 Reachable을 찾아서 Eden이 보낼 때 함께 Survivor 1 (0)영역으로 보내고 sweep함

    • Old 영역
      • Young 영역에서 Reachable 상태를 유지하여 살아남은 객체가 복사되는 영역
      • Young 영역보다 크게 할당되며, 큰 만큼 가비지는 적게 발생하고 GC가 느림 
      • Old 영역이 꽉 찬 경우 Major GC 발생
        • 객체의 age(이동 횟수)가 설정한 임계값에 도달하면 young에서 old 영역으로 이동

 

 

  • 단점
    • Stop the world : GC의 작업을 수행하기 위해 JVM이 어플리케이션의 실행을 잠시 멈추고, GC를 실행하는 쓰레드를 제외한 모든 쓰레드들의 작업을 중단 후 (Stop The World 과정) 사용하지 않는 메모리를 제거(Mark and Sweep 과정)하고 작업 재개

https://inpa.tistory.com/entry/JAVA-☕-가비지-컬렉션GC-동작-원리-알고리즘-💯-총정리

 

 

생성자(Constructor)

생성자는 클래스와 같은 이름의 메소드로, 객체가 생성될 때 호출되는 메소드
명시적으로 생성자를 만들지 않아도 default로 만들어지며, 생성자는 파라미터를 다르게하여 오버로딩할 수 있음

 

 

클래스(Class) vs. 인스턴스(Instance)

클래스는 객체를 정의하여 객체를 만들기 위한 설계도이며, 인스턴스는 클래스로부터 생성된 객체를 의미

인스턴스는 클래스의 구조를 바탕으로 메모리에 할당

 

 

 

Interface와 추상클래스(Abstract Class)

  • Interface
    • 모든 메소드가 추상 메소드
    • 다중 상속 가능
    • 구현 객체의 같은 동작을 보장 (인터페이스를 구현하는 모든 클래스에 대해 특정한 메소드가 반드시 존재하도록 강제함)
  • 추상클래스(Abstract Class)
    • 하나 이상의 추상 메소드를 가진 클래스
    • 다중 상속 불가능
    • 일부 공통 기능을 구현함으로써 자식 클래스의 설계를 간소화할 수 있음 (상속받는 클래스들의 공통적인 로직을 추상화시키고 기능 확장을 위해 사용)
  • 공통점
    • new 연산자로 인스턴스 생성 불가능
클래스를 설계도에 비유한다면 추상클래스는 미완성 설계도에 비유할 수 있다.
클래스가 미완성이라는 것은 단지 미완성 메서드(추상메서드)들 포함하고 있다는 의미이다.
미완성 설계도로 완성된 제품을 만들 수 없듯이 추상클래스로 인스턴스는 생성활 수 없다.
추상클래스는 상속을 통해서 자손클래스에 의해서만 완성될 수 있다.

 

 

상속과 컴포지션(Composition)

  • 상속
    • 부모 클래스의 특성과 행위를 자식 클래스가 물려받는 것
    • "is-a" 관계 모델링 ex. Dog is a Animal
    • 장점 : 코드의 재사용성을 높이고, 변경에 용이
    • 단점 : 강한 결합을 만들어 부모 클래스 변경이 자식 클래스에 영향을 미칠 수 있음
public class dog extends animal{
	//... do something
}

 

  • 컴포지션
    • 한 객체가 다른 객체를 소유하거나 포함하는 방식
    • "has-a" 관계 모델링 ex. Car has a Engine
    • 장점 : 클래스 간의 결합도를 낮추며 변경에 용이
public class Car {
	private Engine engine;
    //.. do something
}

 

 

오버로딩(Overloading)과 오버라이딩(Overriding)

  • 오버로딩(Overloading) : 같은 이름의 메소드를 여러개 정의하는 것, 매개변수의 타입이 다르거나 개수가 달라야 함
  • 오버라이딩(Overriding) : 상위 클래스(부모 클래스)의 메소드를 하위 클래스(자식 클래스)에서 재정의

 

JDBC(Java Data Base Connection)

자바에서 데이터베이스에 접속하고 SQL을 실행할 수 있게 하는 API

DB 연결, SQL 실행 등을 자바 코드 내에서 수행할 수 있음

 

 

Call by Reference와 Call by Value

  • Call by Reference - 메서드 호출 시 인자로 받은 값의 메모리 주소를 전달하여 직접 객체의 값을 수정
  • Call by Value - 값을 복사하여 메서드에 전달하므로 메서드 내의 연산이 원본 데이터에 영향을 주지 않음

 

Static

모든 인스턴스에 걸쳐 공유되며 클래스가 메모리에 로드될 때 생성

클래스가 로딩될 때, 메모리 공간이 할당되어 인스턴스(객체) 생성 없이 바로 사용 가능

GC 관리 영역 밖에 있어 종료될 때까지 메모리에 값이 유지된 채 존재

 

 

Primitive type과 Reference type

  • Primitive type - 기본 데이터 타입으로 정수, 실수, 문자, 논리값 등을 직접 저장하는 타입
  • Reference type - 객체의 참조(주소)를 저장하는 타입 Class, Interface, Array 등

 

 

컬렉션 프레임워크

  • 데이터 집합을 효율적으로 관리하고 조작할 수 있는 클래스의 집합
  • ex) List, Set, Map, Queue 등
    • List : 순서가 있는 데이터의 집합이며, 데이터의 중복 허용
    • Set : 순서가 없는 데이터의 집합이며, 데이터의 중복을 허용하지 않음.
    • Map : 키와 값이 한 쌍으로 이뤄져 있고, 키를 기준으로 중복을 허용하지 않으며, 순서가 없음

 

Wrapper Class

 

Primitive type을 객체로 다루기 위해서 사용하는 클래스

Primitive type의 데이터를 컬렉션 프레임워크 등 객체만을 요구하는 API에 사용할 수 있게 해줌

Primitive type으로 표현할 수 있는 간단한 데이터를 객체로 만들어야 할 경우가 있는데 그러한 기능을 지원하는 클래스

 

사용 이유

  • method에 전달된 argument를 수정하기 위해서 객체로 사용
  • ArrayList, Vector는 객체만 사용

 

자바 빈즈(JavaBeans)

자바에서 재사용 가능한 컴포넌트를 만들기 위해 특정 규칙을 따르는 객체

  • 클래스의 속성은 private로 선언하고, getter/setter 사용하여 접근
  • 파라미터가 없는 생성자 제공
  • Serializable 인터페이스를 구현하여 직렬화 가능

 

 

직렬화(Serialization)

객체를 바이트 스트림으로 변환하는 과정.

변환된 데이터는 파일, DB, 메모리 등에 저장하거나 네트워크를 통해 다른 컴퓨터로 전송할 수 있음

(자바에서 입출력으로는 스트림이라는 데이터 통로를 통해 이동했는데 객체는 바이트형이 아니라서 스트림을 통해 전송이 어려워 바이트 배열로 변환이 필요함)

직렬화된 객체는 역직렬화를 통해 원래 객체 상태로 복원될 수 있음

주로 Serializable 인터페이스를 구현하여 직렬화 

 

 

제네릭(generic)

  • 데이터 타입을 일반화한다(generalize)는 것을 의미. 
  • 클래스 내부에서 개별적으로 타입을 지정하지 않고, 사용할 때에 지정
  • 장점
    • 재사용성 : 여러 타입의 파라미터를 삽입해 코드를 간결하게 하고 재사용성을 높임
    • 컴파일시 에러 발견 : 잘못된 타입이 사용될 수 있는 문제를 컴파일 과정에서 제거할 수 있어 에러를 사전에 방지 
      (generic T 대신에 보통 Object를 쓰게 되는데 이때 어떤 객체가 들어올지 몰라 컴파일 과정에서 에러를 발견하기 어려움 https://inpa.tistory.com/entry/JAVA-☕-제네릭Generics-개념-문법-정복하기)
class Box<T>{
    public T material;
    Box(T material){ this.material = material; }
}

Box<Paper> paperBox = new Box<Paper>(new Paper());
Box<Plastic> plasticBox = new Box<Plastic>(new Plastic());

 

 

접근제한자(public > protected > default > private)

변수 또는 메소드의 접근 범위를 설정하는 키워드

  • public - 제한 없음. 프로젝트 내에서 어디서든지 사용 가능
  • protected - 같은 패키지 내, 다른 패키지에서 상속받아 자손클래스에서 접근 가능
  • default - 같은 패키지 내에서만 접근 가능
  • private - 같은 클래스 내에서만 접근 가능

 

Singleton Pattern (싱글톤 패턴)

특정 클래스의 인스턴스가 하나만 생성되도록 보장하고, 그 인스턴스에 전역 접근을 제공하는 디자인 패턴

이 패턴은 시스템 전반에 걸쳐 하나의 인스턴스만 필요한 경우 사용되며, 예를 들어 설정, 캐싱, 스레드 풀 등의 관리에 유용

 

Factory Pattern (팩토리 패턴)

객체간 의존성을 줄이기 위해 객체의 생성과 데이터 주입만 담당하는 Factory 클래스를 정의하고,
개발 코드 부분에서는 생성된 객체를 가져다 사용함으로서 의존성을 줄이는 방식

Observer Pattern (옵저버 패턴)
어떤 객체의 상태가 변경될 때, 그와 연관된 모든 객체들에게 상태변경을 알리는 디자인 패턴

 

AOP(Aspect-Oriented Programming)

 

관점 지향 프로그래밍으로, OOP에서 독립적으로 분리하기 어려운 부가 기능을 모듈화하는 방식

클래스 내 소스 코드 상에 반복적으로 쓰는 코드들(핵심적인 관심사가 아닌 부가적인 관심사 코드들. ex. 로깅, security 등)이 있는데 이런 부분을 Aspect로 모듈화하고 핵심적인 비즈니스 로직에서 분리하여 재사용하고자 함.

 

애플리케이션의 핵심적인 관심사와 이와 무관한 부가적인 관심사(측면, aspect)를 분리하는 프로그래밍 패러다임입니다. 이를 통해 공통의 기능(예: 로깅, 트랜잭션 관리)을 애플리케이션의 여러 부분에 걸쳐 재사용할 수 있습니다. AOP는 코드의 모듈성을 높이고 유지보수를 용이하게 합니다.

 

 

DAO(Data Access Object)와 DTO(Data Transfer Object)와 VO(Value Object)

  • DAO
    • DB의 데이터에 접근하기 위한 객체로 CRUD 기능 등을 수행
    • DB 로직과 비즈니스 로직을 분리하기 위함
@Repository
@RequiredArgsConstructor
public class MemberRepository {

    private final EntityManager em;

    public void save(Member member) {
        em.persist(member);
    }

    public Member findOne(Long id) {
        return em.find(Member.class, id);
    }

    public List<Member> findAll() {
        return em.createQuery("select m from Member m", Member.class).getResultList();
    }

    public List<Member> findByName(String name) {
        return em.createQuery("select m from Member m where m.name =:name", Member.class)
                .setParameter("name", name)
                .getResultList();
    }
}

 

 

  • DTO
    • 계층 간 데이터 교환을 위해 사용하는 객체 (DB에서 데이터를 얻어서 Service나 Controller 등으로 보낼 때 사용)
    • 로직 없이 getter와 setter만 가진 클래스
    • 데이터 전달 과정에서 데이터의 불변성을 보장
// Entity
@Entity
@Getter
@Setter
public class Member {

    @Id
    @GeneratedValue
    private Long id;

    private String name;
	private int age;

}


// DTO Class
@Getter
@Setter
static class ResponseDto {
    private String name;
    private String result = "결과입니다.";

    public ResponseDto(Member member) {
        name = member.getName();
    }
}


// Controller
@PostMapping("api/useDTO")
@ResponseBody
public ResponseDto useDTO(@RequestParam String name, @RequestParam Long id) {
    Member findMember = memberService.findOne(id);
    return new ResponseDto(findMember);
}

 

  • VO
    • 값 자체를 표현하는 객체로 getter만 가지고 있어 수정이 불가능
    • 두 객체에서 모든 필드 값들이 동일하면 두 객체는 같음 (equal, hashCode 등 구현)
@Getter 
@Setter
@Alias("article")
class ArticleVO {
    private Long id;
    private String title;
    private String contents;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Article article = (Article) o;
        return Objects.equals(id, article.id);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
}

 

 

Error와 Exception

  • Error : 실행 중 일어날 수 있는 치명적 오류
    • 컴파일 시점에 체크할 수 없고, 오류가 발생하면 프로그램은 비정상 종료되며 예측 불가능한 UncheckedException에 속함
  • Exception : Error보다 비교적 경미한 오류
    • try-catch를 이용해 프로그램의 비정상 종료를 막을 수 있음

 

리플렉션(Reflection)

구체적인 클래스 타입을 알지 못해도 그 클래스의 메소드, 타입, 변수들에 접근할 수 있도록 해주는 자바 API

런타임 시에 클래스의 정보를 분석하고, 객체를 생성하거나 메소드를 호출하는 등의 작업을 동적으로 수행할 수 있음

단, 런타임에 동작하기 때문에 성능이 떨어질 수 있으며 코드 가독성이 떨어짐

Class<?> clazz = MyClass.class;
// Class<?> clazz = Class.forName("com.example.MyClass");
 
String className = clazz.getName(); // 클래스 이름 가져오기
Field[] fields = clazz.getDeclaredFields(); // 클래스 필드 가져오기
Method[] methods = clazz.getDeclaredMethods(); // 클래스 메소드 가져오기

 

 

 

String, StringBuffer, StringBuilder

  • String
    • 불변 : 한번 할당된 공간이 변하지 않음
      • Hello에서 World를 더하면 Hello World 값을 저장한 영역을 따로 만들어 해당 공간을 참조하는 식으로 변경
    • 장점
      • String pool에 각 문자열 하나만 저장하여 다시 사용하거나 캐싱 가능 → 힙 공간 절약
      • 동기화 : 불변이면 멀티 스레드 환경에 값이 변경될 위험이 없어서 자연스럽게 thread-safe한 특성을 가짐
    • 단점 : 초기공간과 다른 값에 대한 연산에서 많은 시간과 자원 사용

 

더보기
  • String pool
    • string 타입이 빈번하게 사용되어서 string pool 개념을 만들어, 매번 string 객체를 새로 생성하기 보다 값이 값은 string이라면 string pool에 있는 객체를 재사용할 수 있도록 구현
    • 이렇게 공유를 하기 위해서 string은 반드시 불변이어야 함

 

  • StringBuffer와 StringBuilder는 가변 (객체의 공간이 부족할 경우 크기 유연하게 변경)
    • 내부 Buffer(임시 저장 메모리)에 문자열을 저장하여 그 안에서 연산하여 속도 빠름 
    • StringBuffer는 동기화를 지원하여 멀티 쓰레드 환경에서 주로 사용
      • 메서드에서 sychronized 키워드 사용
    • StringBuilder는 동기화를 지원하지 않아 싱글 쓰레드 환경에서 주로 사용

 

 

new String()과 리터럴("")

자바에서 String을 선언하는 두 가지 방법으로 실제 메모리에 할당되는 영역 차이가 있음

 String str1 = new String("madplay"); // new 연산자 방식
 String str2 = "madplay";			// 리터럴 방식

 System.out.println("str1 == str2 : " + (str1 == str1));  // false
  • new String()은 new 키워드로 새로운 객체를 생성하기 때문에 Heap 메모리 영역에 저장
  • ""는 Heap 안에 있는 String Constant Pool 영역에 저장

 

 

Synchronized (thread 동기화)

  • thread 동기화 : 멀티스레드 환경에서 여러 thread가 하나의 자원에 동시에 접근하지 못하도록 막는 것
  • 동기화가 필요한 메소드나 코드 블럭 앞에 Synchronized 키워드를 작성하며 한 thread가 사용할 때 lock
  • Synchronized는 변수와 메소드에 사용해서 동기화 할 수 있으며, Synchronized 키워드를 남발하면 성능 저하
synchronized void increase() {
	count++;
	System.out.println(count);
}
반응형