1. Build system이란?

image

빌드 시스템의 도입 목적은 프로젝트의 IDE 독립성을 보장하는 것이다. 개발자는 각자 익숙한 IDE를 선택해 사용할 수 있으며, 특정 IDE에 종속되지 않도록 하기 위해 빌드 시스템을 사용한다.

  • 예를 들어 팀장이 VS(비주얼 스튜디오)로 프로젝트를 생성하고 git에 업로드했다. 팀원들이 해당 프로젝트를 내려받아 개발을 시작할 때 반드시 VS를 사용해야 한다. 다른 ide를 사용하려고 하면 프로젝트의 빌드 설정을 다시 해야 하며 이로 인해 추가적인 버그가 발생할 수도 있다.
    • 해당 문제를 해결하기 위해 빌드 설정은 특정 ide가 아닌 빌드 도구 (gradle, maven, cmake) 등을 이용해 관리한다.
    • 이제 소스코드와 빌드 설정한 git에 올리면 개발자는 각자 원하는 ide로 작업을 이어갈 수 있다.

      IDE의 독립성을 위해 빌드시스템이 따로 존재.

2. Runtime

자바로 만든 모든 애플리케이션을 실행하기 위해서는 실행기=runtime이 필요하다. 이 실행기가 바로 Java Virtual Machine(JVM)이다.

🍁 JVM (Java Virtual Machine)

자바 프로그램을 실행하는 가상환경이다. 자바 코드(.java)를 컴파일하여 생성된 바이트코드(.class)를 읽고 이를 운영체제에서 실행 가능한 코드로 변환한다. 운영체제에 종속되지 않도록 하기 때문에 한번 작성된 자바 프로그램은 다양한 운영체제에서 실행 가능하다. (할머니 집 오래된 노트북에서도 자바 프로그램이 실행되는 이유, 윈도우에 JVM이 내장)

WORA(Write Once, Run Anywhere) = 자바의 가장 큰 장점, 운영체제에 독립적이다.

🍁 JDK (Java Development Kit)

JDK는 JVM뿐만 아니라 개발에 필요한 다양한 도구들을 포함한 개발 키트이다. 컴파일러(javac), 디버거, 빌드 도구 등이 포함된다.

  • JDK = JVM(실행기) + 개발도구로 애플리케이션을 개발하고 실행할 때 필요한 환경을 모두 갖춘다.
  • 각 JDK 버전에는 차이가 있어 프로젝트를 진행하는 팀원 모두 같은 JDK 버전을 사용해 개발환경을 맞춰야 한다. (java 8, java 11…)

+. 자바스크립트도 자바처럼 실행 환경, 실행기가 필요하다. »> Node.js

3. 래퍼 클래스

래퍼 클래스는 자바의 기본 데이터 타입 (primitive type)을 객체 형태로 감싸는 클래스이다. primitive 타입에는 int, char, double, boolean 등이 있다. 이러한 원시 타입은 객체가 아니기 때문에 객체가 필요한 상황 (컬렉션, 제네릭, null 처리 등)에서는 사용할 수 없다. 이를 해결하기 위해 기본 타입을 감싸는 래퍼 클래스를 제공한다.

Integer / Character / Double / Boolean

  • 래퍼 클래스는 기본 타입에 추가적인 메서드(예: parseInttoStringcompareTo 등)를 제공하여, 기본 타입을 객체처럼 사용할 수 있도록 해줍니다.
String numberStr = "12345"; // 문자열을 정수형으로 변환 
int number = Integer.parseInt(numberStr);

int num = 100; // 정수형 값을 문자열로 변환 
String numStr = Integer.toString(num);

4. 안전한 API 만들기

  1. 가드절(guard clause)
  2. 인자 검사(인자가 정상값 범주 안에 있는지 검사)
  3. 유효성 검사
  4. precondition check 이러한 방법들을 사용해 입력 값이 예상치 못한 경우 프로그램이 올바르게 작동하지 않는 문제를 방지하고, 에러를 미리 감지해 안전한 코드를 짤 수 있게 한다.

5. 상속

자식은 부모가 가진 요소들을 다 복사해온다. 사실 부모 자식 관계로 명명하면 안된다. 사실 분류와 종류 관계다. Super Sub / Base Drived(파생) 상속의 궁극적인 목적은 중복 코드를 줄이기 위해서다.

Robot p = new SpeedRobot();
p.speedPrint(); // 겁나빨라

SpeedRobot s = new Robot();
// s.speedPrint(); 오류

Robot ro2 = new Speed();  
// ro2.speedUp(); 오류

생성할 때 오른쪽 대입이 더 커야 함. 큰 건 sub(자식) 사용할 때 왼쪽 선언까지만 인스턴스에서 사용 가능

6. Simple Factory Method Pattern

// 1. TV 인터페이스 정의
interface Remote {
    void powerOn();
}

// 2. SamsungTV 클래스
class SamsungTV implements Remote {
    @Override
    public void powerOn() {
        System.out.println("Samsung TV가 켜졌습니다.");
    }
}

// 3. LGTV 클래스
class LGTV implements Remote {
    @Override
    public void powerOn() {
        System.out.println("LG TV가 켜졌습니다.");
    }
}

// 4. Factory 클래스 - 팩토리 메서드
class Factory {
    public Remote makeTV(String brand) {
        if (brand.equalsIgnoreCase("Samsung")) {
            return new SamsungTV();
        } else if (brand.equalsIgnoreCase("LG")) {
            return new LGTV();
        } else {
            throw new IllegalArgumentException("해당 브랜드의 TV는 없습니다.");
        }
    }
}

// 5. TV 클래스
class TV {
    public void tvOn(Remote remote) {
        remote.powerOn();
    }
}

// 6. Main 클래스 - 클라이언트 코드
public class Main {
    public static void main(String[] args) {
        // 팩토리 객체 생성
        Factory factory = new Factory();
        
        // Samsung TV 생성 및 켜기
        TV tv1 = new TV();
        Remote samsungTV = factory.makeTV("Samsung");
        tv1.tvOn(samsungTV);
        
        // LG TV 생성 및 켜기
        TV tv2 = new TV();
        Remote lgTV = factory.makeTV("LG");
        tv2.tvOn(lgTV);
        
        // 예외 상황 - 지원되지 않는 브랜드
        try {
            Remote unknownTV = factory.makeTV("Sony");
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage());
        }
    }
}

  • Remote : 모든 TV가 구현할 공통 메서드
  • Factory 클래스는 안에 new를 몰빵한다. 이를 통해 의존성을 한 곳에서 관리하도록 한다.
    • new 키워드를 사용하는 객체 생성 책임을 한 곳에서 집중시킨다.
  • TV 클래스는 의존성 주입으로 인자로 Remote를 받아서 삼성과 TV, 엘지와 TV 클래스 사이의 의존성을 없앤다. 직접 의존하지 않는다. 이를 통해 결합도를 낮춘다.
    • 이후 새로운 tv 브랜드가 생겨도 TV 클래스는 변경할 필요가 없다. 팩토리만 수정하면 된다.

      “의존성 주입(Dependency Injection, DI)이란 클래스 사이의 연결통로를 만들어서 서로 간접적으로 연결해주고 그 각 클래스들 사이의 의존관계를 낮추는 것이다”

      즉 한 클래스가 다른 클래스에 직접적으로 의존하지 않도록 한다.

7. IoC 컨테이너

image

  1. 기본 객체 생성과 소멸관리
    • 일반적으로는 앺이 직접 객체를 생성하고 소멸을 관리한다. new를 통해 객체를 생성하고 객체 사용이 끝나면 가비지 컬렉터 GC가 객체를 소멸시키는 방식으로 관리한다.
    • 즉 앺이 객체 소멸 및 제어권을 가지고 있다.
  2. 프레임워크의 역할 : 제어권의 역전 IoC
    • 스프링과 같은 프레임워크는 객체의 생성과 소멸에 대한 제어권을 가져간다. 이를 제어의 역전 (Inversion of Control, IoC) 라고 한다.
    • 개발자가 직접 객체를 생성, 소멸시키는 대신 필요한 객체를 프레임워크에 요청하고 프레임워크는 IoC 컨테이너를 통해 관리된 객체 (빈, Bean) 을 제공한다.

      IoC 컨테이너가 프레임워크의 팩토리=new 몰빵한 곳

  3. 객체 Bean 관리 방식
    • 프레임워크는 미리 많은 객체를 만들어서 관리한다. 또 개발자=클라이언트가 객체를 반환하면 이를 재사용해 더 효율적으로 사용하도록 관리한다.
      • 이렇게 미리 만들어진 객체를 Bean이라 한다.
    • 객체 풀을 운영하여 한번에 많은 객체를 배열로 생성한다. 필요할 때 클라이언트에게 제공하 고 객체 사용이 끝나면 다시 풀로 반환받아 재사용한다.
      • 이를 통해 GC의 동작을 줄이고 자원을 절약한다.
    • 정기적으로 정리작업 (Garbage Collection 및 자원 최적화)를 수행해 객체 관리를 효율적으로 한다.
  4. 클라이언트=앺에서 하는 일
    • 앺은 프레임워크가 관리하는 객체의 생성과 소멸에 신경 쓸 필요가 없어졌다.
      • 이런 정보들은 이제 앺에서는 전혀 몰라도 되는 정보들
      • 앺에서는 객체 요청, 반환 두 개만 하면 된다.
    • 앺은 단순히 객체가 필요하면 IoC 컨테이너에 Bean 객체를 받아 사용하기만 하면 된다.
    • 객체 사용이 끝나면 반환한다.

댓글남기기