Java/Spring 관련 질문 정리 - 1

망7H·2021년 5월 2일
1

1. OOP

Object Oriented Programming를 의미하며 프로그래밍의 방법론 중 하나입니다.
객체의 상호작용을 통해 프로그래밍하는 것을 말하는데 캡슐화, 상속, 추상화, 다형성 등의 4가지 특징을 이용해서 코드의 재사용성을 증가시키고 유지보수를 감소시키는 장점을 얻는 특징이 있습니다.

1) 캡슐화(Encapsulation)

외부 객체가 내부 객체의 구조를 알 수 없고, 내부 객체가 제공하는 필드와 메소드만 이용 가능.
이러한 정보의 은닉화를 위해 접근제어자와 getter, setter를 사용 가능.

public class User {
  private String name;
  
  public String getName() {
    return name;
  }
  
  public void setName(name) {
    this.name = name;
  }
}

외부 객체에서는 내부 객체(User)의 name 이라는 멤버변수에 직접적인 접근은 불가능하고, 내부 객체(User)가 제공하는 메서드를 통해서만 접근 가능.

2) 상속(Inheritance)

상위 클래스의 특징을 하위 클래스가 물려받는 것.
하위 클래스는 상위 클래스로부터 물려받은 메서드를 바로 사용하거나, 내부의 동작을 바꾸는 오버라이딩(overriding)을 할 수도 있음.

3) 추상화(Abstraction)

클래스 간의 공통적인 특징을 찾아내어 공통의 상위 클래스(= 추상 클래스 or 미완성 클래스)를 만드는 과정이라고 할 수 있음.

4) 다형성(Polymorphism)

여러 가지 형태를 가질수 있는 능력을 의미하고,
자바에서는 하나의 타입의 참조변수가 여러 타입의 객체(인스턴스)를 참조할 수 있도록하는 것.

※ 참조변수 타입과 인스턴스에 따른 멤버변수와 메서드의 사용
Child.class가 Parent.class를 상속받는다고 가정할 때
부모 타입의 참조변수가 자식 타입의 인스턴스를 참조하는 경우
1) 메서드는 실제 인스턴스(자식 타입)의 것을 사용한다.
ㄴ 오버라이딩 되었을 수도 있으니 라고 생각하면 편하다.
2) 멤버변수는 참조변수의 타입(부모 타입)의 것을 사용한다.
ㄴ Child와 Parent에 동일한 이름의 멤버변수가 있더라도 참조변수의 타입

2. Reflection

객체를 통해 클래스의 정보를 분석해내는 프로그램 기법을 의미한다.
구체적인 클래스 타입을 알지 못해도, 그 클래스의 메서드, 멤버변수 등에 접근할 수 있도록 해주는 기법이다.

참고로, 스프링 프레임워크에서도 이러한 Reflection 기법을 사용한다.
스프링 컨테이너(ApplicationContext.class)의 최상위 인터페이스로 BeanFactory.class(이하, BeanFactory)를 갖는데, BeanFactory는 빈을 관리하고 조회하는 역할을 담당한다.
빈은 애플리케이션이 실행되는 런타임에 객체가 호출될 때 동적으로 객체의 인스턴스를 생성하는데, 이때 BeanFactory에서 Reflection API를 사용한다.

예를 들어보자.

@Repository
public class MemberRepository {
   @Autowired
   private MemberService memverSevice;
   ...
}

Member.class의 바로 위에 @Repository를 붙여주면 인스턴스를 생성하지 않아도 스프링이 알아서 빈으로 관리해준다.
Q) 스프링이 MemberRepository를 어떻게 알고 빈으로 관리해주는 것일까?
Q) 스프링이 멤버변수 MemberService를 어떻게 주입해주는 건지?
이러한 질문들에 대한 답변으로 스프링은 애플리케이션의 런타임에 Reflection API를 사용하여 MemberRepository의 정보를 알아내고, 지정한 설정(어노테이션)에 따라 동적으로 처리해준다는 것.

정리하면 위의 예제로 보면 스프링은 MemberRepository의 구체적인 클래스 타입은 모르지만,
런타임 시점에 Reflection API를 통해 해당 클래스의 메서드나 멤버변수에 접근하여 정보를 알아낼 수 있고 동적으로 처리할 수 있다는 것이다.

3. JVM과 JVM 메모리 구조

JVM은 자바를 실행하기 위한 가상의 기계라고 할 수 있는데, Java Byte Code를 각 OS에 맞게 해석해주는 역할을 한다.
JVM의 메모리 구조는 아래와 같다.
애플리케이션에 실행되면 JVM은 시스템으로부터 애플리케이션을 수행하는데 필요한 메모리를 할당받고 이 메모리를 용도에 따라 여러 영역으로 나누어 관리한다.
Method Area, Call Stack, Heap은 주요 3가지 영역이다.

1) Method Area

프로그램 실행 중 어떤 클래스가 사용되면, JVM은 해당 클래스의 클래스 파일을 읽고 분석하여 클래스에 대한 정보를 이 영역(Method Area)에 저장한다.
이때, 그 클래스의 클래스 변수(Static Variable)도 이 영역에 저장한다.

2) Heap

인스턴스가 생성되는 공간으로 프로그램 실행 중 생성되는 인스턴스는 모두 이곳에 생성된다.
(즉, new 연산자를 통한 동적 할당된 객체들이 저장되는 공간.)
인스턴스 변수(Instance Variable)이 생성되는 공간이며, 인스턴스가 더이상 사용되지 않는 경우에는 가비지 컬렉션에 의해 메모리가 관리되어 진다.

(1) 메모리 상수 풀

Heap 영역에 생성되어 java 프로세스 종료까지 계속 유지되는 메모리 영역으로, 프로그래머가 작성한 상수에 대해 최우선적으로 찾아보고 없으면 상수풀에 추가하여 그 주소값을 반환한다.
(메모리 상수 풀을 사용함으로써 메모리 절약 효과가 있다.)

3) Call Stack

Call Stack은 메서드의 작업에 필요한 메모리 공간을 제공한다.
메서드가 호출되면, 호출스택에 호출된 메서드를 위한 메모리가 할당되며, 이 메모리는 메서드가 작업을 수행하는 동안 지역변수들과 연산의 중간결과 등을 저장하는데 사용된다.
마지막으로 메서드가 작업을 마치면 할당되었던 메모리 공간은 반환되어 비워진다.

4. 변수의 초기화 순서

1) 클래스 변수(Static Variable)

명시적 초기화 → 클래스 초기화 블럭

class Product {
  // 클래스 명시적 초기화
  static int price = 10000;
  
  // 클래스 초기화 블럭
  static { price = 15000; }
}

// price history: 10000 → 15000
// 최종적으로 price는 15000 으로 초기화 됨.

2) 인스턴스 변수(Instance Variable)

명시적 초기화 → 인스턴스 초기화 블럭 → 생성자

class Product {
  // 인스턴스 명시적 초기화
  int price = 10000;
  
  // 인스턴스 초기화 블럭
  { price = 15000; }
  
  // 생성자
  Product() { price = 20000; }
}

// price history: 10000 → 15000 → 20000
// 최종적으로 price는 20000 으로 초기화 됨.

5. Spring 특징

스프링이란?
자바 엔터프라이즈 개발을 위한 오픈소스 애플리케이션 프레임워크라고 한다. (너무 길다)
결국 프레임워크이니 개발할 때 설계의 기본이 되는 뼈대나 구조, 환경 정도라고 보면 되겠다.
다만, 스프링에서는 몇 가지 중요한 특징이 있다.
※ 아래의 예시에서 사용하는 클라이언트와 서버라는 표현은 실제 User와 서버를 의미하는 것이 아닌 상대적 의미로 사용한 것이다. 객체를 호출하는 쪽을 client, 호출받는 객체를 server로 표현한 것이다.

1) DI (Dependency Injection)

애플리케이션 실행 시점에 외부에서 실제 구현 객체를 생성하고 클라이언트에 전달해서 클라이언트와 서버의 실제 의존관계가 연결되는 것을 DI(의존관계 주입)이라고 한다.

먼저, '의존관계를 가진다'라는 표현을 아래와 같은 클래스를 보면서 이해해보자.

// 할인 정책 interface
public interface DiscountPolicy {
  ...
}

// 할인 정책을 구현한 '정률' 할인정책
public class RateDiscountPolicy implements DiscountPolicy {
  ...
}

public class OrderServiceImpl implements OrderService {
  private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
  ...
}

OrderServiceImpl은 인터페이스 DiscountPolicy에 의존하고, 구현체인 RateDiscountPolicy에도 의존하고 있음을 볼 수 있다.
위와 같은 상황을 'OrderServiceImplDiscountPolicyRateDiscountPolicy에 의존 관계를 가진다'라고 표현할 수 있다.

이러한 의존관계를 없애기 위해서

// 할인 정책 interface
public interface DiscountPolicy {
  ...
}

// 할인 정책을 구현한 '정률' 할인정책
@Componenet
public class RateDiscountPolicy implements DiscountPolicy {
  ...
}

@Component
public class OrderServiceImpl implements OrderService {
  @Autowired
  private DiscountPolicy discountPolicy;
  ...
}

위와 같이 OrderServiceImpl이 인터페이스 DiscountPolicy에만 의존하고,
구현체로 어떤것을 주입받더라도 상관없게 단순히 의존성이 주입되도록만 해줌으로써
객체지향의 5대 원리 중 OCP와 DIP를 지키기 용이하다.

2) IoC (Inversion of Control)

기존 프로그램에서의 제어 흐름은 클라이언트 구현 객체가 스스로 필요한 서버 구현 객체를 생성하고, 연결하고, 실행하는게 자연스러운 흐름이었다.
이러한 흐름에서 프로그램의 제어 흐름을 관리하는 별도의 객체를 만들고, 외부에서 관리하는 것을 IoC(제어의 역전)이라고 한다.

위의 '1) DI (Dependency Injection)'에서 주입해줄 객체를 외부에서 관리해주는 소스를 아래와 같이 추가할 수 있다.

@Configuration
public class AppConfig {
  @Bean
  public DiscountPolicy discountPolicy() {
    return new RateDiscountPolicy();
  }
}

@Component
public class OrderServiceImpl implements OrderService {
  @Autowired
  private DiscountPolicy discountPolicy;
  ...
}

서버 구현 객체에서 DiscountPolicy 타입의 변수에 @Autowired를 하게되면 위와 같이 외부에서 객체의 주입을 제어해줄 수 있다.
이렇게 프로그램의 제어 흐름을 서버 구현 객체에서 직접 제어하는 것이 아니라, 외부(AppConfig 같은)에서 관리하는 것을 IoC(제어의 역전)이라고 한다.

3) AOP (Aspect Oriented Programming)

이전에는 공통 기능을 모듈에 적용하기 위해 상속을 많이 이용했지만, Java에서는 다중 상속이 불가능하다. 결국 공통의 기능을 작성하고, 이를 수정하기 위해서는 도처에 널려있는 다양한 소스를 직접 수정해주어야 하였다.

스프링에서는 핵심 관심 사항과 공통 관심 사항을 분리하여, 여러 곳에서 쓰이는 공통 기능을 모듈화하고, 쓰이는 곳에 필요할 때 연결함으로써 재사용과 유지보수에 용이하도록 프로그래밍하는 기법을 AOP라고 한다.

참고로, 여기서 java의 클래스에 AOP를 적용하게 되면 java는 해당 객체를 직접적으로 호출하는 것이 아니라, 해당 객체의 프록시를 먼저 호출하여 마치 '해당 객체'를 호출한 것처럼 앞서 한번 더 호출하는 내부 동작이 있다.

6. MVC

MVC는 애플리케이션의 핵심적인 비즈니스 로직을 담당하여 데이터를 관리하는 Model과 사용자에게 보여지는 화면 View, 그리고 ModelView 사이에서 정보 교환을 할 수 있도록 컨트롤 해주는 Controller를 의미하는 디자인 패턴입니다.



참고

https://gyrfalcon.tistory.com/entry/Java-Reflection
https://dublin-java.tistory.com/53
https://jseoposh.tistory.com/9

profile
망한 개발자의 개발 기록입니다. 저를 타산지석으로 삼으시고 공부하세요.

0개의 댓글