1. FeignClient란 ? 

Spring Cloud에서 제공하는 선언적 HTTP 클라이언트

Netflix에서 간단하게 HTTP Client를 사용하기 위해 개발한 기술
RestTemplate, WebClient를 쓸 때보다 훨신 가독성있고 편하게 사용 가능 
HTTP 기반의 외부 서비스와 통신하기 위한 REST 클라이언트를 간단하게 생성할 수 있도록 도와주는 라이브러리

* WebClient : 
Spring webflux의 라이브러리, non-blocking 방식으로 동작 (대용량 트래픽)

2. FeignClient의 주요 특징

1. 선언적 API 호출: 
FeignClient는 HTTP 요청을 메서드 인터페이스로 선언할 수 있어, 
HTTP 요청을 직접 만드는 코드나 RestTemplate을 사용하는 것보다 
코드가 간결하고 읽기 쉬운 방식으로 API를 호출 가능

2. 자동 retry 및 로드 밸런싱: 
Spring Cloud Feign은 Ribbon과 통합되어 자동으로 로드 밸런싱을 제공하며, 
Hystrix와 결합해 장애 처리 및 retry 메커니즘을 지원

3. 템플릿 기반 요청: 
@FeignClient 인터페이스에서 메서드를 선언하고, 이를 기반으로 HTTP 요청을 템플릿 방식으로 처리

4. 유연한 커스터마이징: 
각종 인터셉터나 오류 처리 메커니즘을 쉽게 추가하여 API 호출 로직을 커스터마이징 가능

3. 기본 사용법

더보기

1. 의존성추가 : 

Spring Cloud OpenFeign

 

2. OpenFeign 관련 컴포넌트 스캔 활성화 : 

@EnableFeignClients 또는 configration 파일 지정된 패키지에서 Feign 클라이언트 인터페이스를 스캔하고, 해당 인터페이스에 대한 구현체를 생성

 

3. FeignClient 구현 (인터페이스 작성) :

구현체는 크게 Feign의 Default, ApacheHttpClient, OkHttpClient

ApacheHttpClient와 OkHttpClient는 추가적인 의존성을 주입해줘야 사용가능

feign.Client.Default는 Feign에서 기본으로 제공, 의존성 주입 없이 사용가능

Default는 내부적으로 java.net의 HttpUrlConnection을 사용, 가볍고 빠르다는 장점

ApacheHttpClient와 OkHttpClient는 Default 구현체보다 설정할 수 있는 값들이 더 많고, 더 편리한 api들을 제공

 

4. dto, controller 등 작성

3-1. FeignClient 인터페이스 정의

@FeignClient(name = "user-service")
public interface UserServiceClient {

    @GetMapping("/users/{id}")
    User getUser(@PathVariable("id") Long id);

    @PostMapping("/users")
    User createUser(@RequestBody User user);
}

3-2. FeignClient 호출

@Service
public class UserService {

    @Autowired
    private UserServiceClient userServiceClient;

    public User getUser(Long id) {
        return userServiceClient.getUser(id);
    }

    public User createUser(User user) {
        return userServiceClient.createUser(user);
    }
}

4. 추가 기능 (errorrecoder, decoder , interceptor, retry)

4-1. RequestInterceptor

Feign 요청을 가로채서 수정하거나, 공통 헤더 , 로깅을 추가하는 등의 작업을 할 수 있습니다.
RequestInterceptor를 사용하여 요청을 인터셉트할 수 있습니다.

* RequestIntercepor : @interface, @Override apply 


@Bean
public RequestInterceptor requestInterceptor() {
    return new RequestInterceptor() {
        @Override
        public void apply(RequestTemplate template) {
            // 헤더에 인증 토큰 추가
            template.header("Authorization", "Bearer " + token);
        }
    };
}

@FeignClient(name = "user-service", configuration = FeignConfig.class)
public interface UserServiceClient {
    @GetMapping("/users/{id}")
    User getUser(@PathVariable("id") Long id);
}

4-2. ErrorDecoder

Feign 클라이언트를 통해 요청을 보냈을 때 HTTP 응답 코드가 실패한 경우에 처리할 로직을 정의하는 데 사용 
spring cloud openfeign은 ErrorDecoder를 통해 응답값이 200이 아닐 경우 에러 
400, 404, 500 등의 오류가 발생하면 FeignException을 던지고, 이는 기본적으로 5xx 예외로 처리

public class MyErrorDecoder implements ErrorDecoder {
    @Override
    public Exception decode(String methodKey, Response response) {
        if (response.status() == 404) {
            return new NotFoundException("Resource not found");
        } else if (response.status() == 500) {
            return new InternalServerErrorException("Internal server error");
        }
        return FeignException.errorStatus(methodKey, response);
    }
}

@FeignClient(name = "user-service", configuration = MyErrorDecoder.class)
public interface UserServiceClient {
    @GetMapping("/users/{id}")
    User getUser(@PathVariable("id") Long id);
}

4-3. Decoder

Feign 클라이언트가 서버에서 받은 응답을 객체로 변환하는 역할

 기본적으로 JSON을 Java 객체로 변환하기 위해 Jackson을 사용하지만, 
 다른 포맷(예: XML, YAML 등)을 처리하려면 Decoder를 구현하여 변환 로직을 커스터마이즈
 
 public class MyCustomDecoder implements Decoder {
    private final JacksonDecoder jacksonDecoder = new JacksonDecoder();

    @Override
    public Object decode(Response response, Type type) throws IOException {
        // 커스텀 디코딩 로직
        if (type.equals(MyCustomType.class)) {
            return customDecodeLogic(response);
        }
        return jacksonDecoder.decode(response, type); // 기본 Jackson 디코더 사용
    }
}

@Configuration
public class FeignConfig {
    @Bean
    public Decoder decoder() {
        return new MyCustomDecoder();
    }
}

@FeignClient(name = "user-service", configuration = FeignConfig.class)
public interface UserServiceClient {
    @GetMapping("/users/{id}")
    User getUser(@PathVariable("id") Long id);
}

4-4. Retryer 

Feign 클라이언트가 요청을 보냈을 때 
일시적인 오류(예: 네트워크 오류 등)가 발생하면 자동으로 재시도할 수 있게 해줌,  
재시도할 횟수와 대기 시간 등을 설정

public class MyRetryer implements Retryer {
    private int attempt = 1;
    private static final int MAX_ATTEMPTS = 3;

    @Override
    public void continueOrPropagate(RetryableException e) {
        if (attempt++ > MAX_ATTEMPTS) {
            throw e;
        }
        try {
            Thread.sleep(1000); // 재시도 간 대기 시간
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
        }
    }

    @Override
    public Retryer clone() {
        return new MyRetryer();
    }
}

@Configuration
public class FeignConfig {
    @Bean
    public Retryer retryer() {
        return new MyRetryer();
    }
}

@FeignClient(name = "user-service", configuration = FeignConfig.class)
public interface UserServiceClient {
    @GetMapping("/users/{id}")
    User getUser(@PathVariable("id") Long id);
}

 

더보기

ex. RestTemplate

참고 : 

https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/

https://techblog.woowahan.com/2630/

https://techblog.woowahan.com/2657/

https://mangkyu.tistory.com/278

https://mangkyu.tistory.com/279

'Spring' 카테고리의 다른 글

ArgumentResolver  (0) 2025.01.19
디버깅 모드  (0) 2025.01.19
멀티 모듈 (Multi-Module)  (0) 2025.01.17
API(application programming interface), RestAPI  (0) 2023.05.22
웹 동작방식, HTTP, WAS, Web Server  (0) 2023.05.21

불변 객체(Immutable Object) : 한 번 생성되면 내부 상태(필드 값)를 변경할 수 없는 객체

 즉, 객체가 생성된 이후에는 해당 객체의 상태를 변경하는 어떤 메서드나 동작도 존재하지 않음

 

대표적인 불변 객체의 예:

String 클래스

Integer, Long, Double 등 기본형 래퍼 클래스

 

불변 객체의 장점

1. 내부 상태 변경 불가
	객체가 생성된 이후에는 그 값이 변경되지 않음
	모든 필드는 final로 선언되어야 하며, 생성자에서만 초기화 가능
	
2. 가비지 컬렉션의 성능을 높일 수 있음 
	불변객체를 이용하면 GC의 스캔빈도와 범위가 줄게되어 GC의 성능에 도움이 됨
    
3. equals와 hashCode의 안정성 보장
	객체의 상태가 변하지 않기 때문에, 해시 기반 컬렉션(HashMap, HashSet 등)에서 안전하게 사용가능
    
4. 스레드 안전성(Thread-Safety) 
	불변 객체는 항상 동일한 값을 보장하므로 동기화를 신경쓸 필요가 없음
    병렬 프로그래밍에 유용함
* 멀티 스레드 환경에서는 공유 자원에 대해 서로 변경하면 값이 덮어씌워지는 문제가 발생

 

불변 객체를 선호하고 중요하게 여기는 이유

1.	스레드 안전성
	불변 객체는 여러 스레드에서 동시에 사용되더라도 상태가 변경되지 않으므로 
    동기화 코드 없이 안전하게 사용할 수 있다
	ex. String 클래스는 스레드 간 안전하게 공유 가능
2.	안정성과 신뢰성
	객체의 상태가 변하지 않으므로 한 번 설정된 값은 변하지 않는다
	디버깅과 유지보수가 용이
3.	캡슐화 강화
	불변 객체는 내부 상태가 외부에 노출되지 않도록 설계되기 때문에 캡슐화 원칙을 강화
	불변성을 유지하기 위해 외부에서 내부 필드에 접근할 수 없도록 방지
4.	캐싱과 재사용 가능
	불변 객체는 상태가 변하지 않으므로 동일한 상태를 가진 객체를 캐싱하여 재사용 가능
	ex. Integer 클래스의 valueOf 메서드는 -128부터 127 사이의 값을 캐싱하여 성능을 최적화
5.	컬렉션에서 안정적 동작
	불변 객체는 HashMap, HashSet 등의 컬렉션에서 안정적으로 동작
	해시 기반 컬렉션에서는 객체의 상태가 변경되면 해시 충돌이 발생할 수 있는데, 불변 객체는 이런 문제를 방지

 

불변 객체의 단점

1.	초기화 시 성능 비용
	불변 객체를 새로 생성하려면 기존 객체를 복사하여 새로운 객체를 생성해야 하므로 메모리와 CPU 비용이 증가할 수 있음
	다만, 작은 크기의 객체나 캐싱 기법을 통해 이러한 비용을 줄일 수 있음
2.	메모리 사용량 증가
	상태가 변할 때마다 새로운 객체를 생성하므로, 가변 객체보다 더 많은 메모리를 소비할 수 있음
	* 주로 대량의 데이터를 처리할 때 문제

 

불변 객체 설계 방법

1. 모든 필드를 final로 선언:

private final String name;
private final int age;

2. 클래스를 final로 선언 (상속 방지):

public final class Person {
    // 필드 및 생성자
}

3. 생성자를 통해 필드를 초기화:

public Person(String name, int age) {
    this.name = name;
    this.age = age;
}

4. 필드에 대한 getter는 원시값이나 불변 객체만 반환:

public String getName() {
    return name;
}


5. 참조 타입 필드는 복사본을 반환:

private final List<String> hobbies;

public Person(String name, int age, List<String> hobbies) {
    this.name = name;
    this.age = age;
    this.hobbies = new ArrayList<>(hobbies); // 방어적 복사
}

public List<String> getHobbies() {
    return Collections.unmodifiableList(hobbies); // 불변 리스트 반환
}

불변 객체의 활용 예시
1. 불변 객체 클래스

public final class Person {
    private final String name;
    private final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + '}';
    }
}


2. 사용 예

public class Main {
    public static void main(String[] args) {
        Person person = new Person("Alice", 25);

        System.out.println(person); // 출력: Person{name='Alice', age=25}

        // 상태 변경 불가
        // person.setName("Bob"); // 컴파일 에러 발생
    }
}

 

 

참고 : 

https://devoong2.tistory.com/entry/Java-%EB%B6%88%EB%B3%80-%EA%B0%9D%EC%B2%B4Immutable-Object-%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90

 

'Java' 카테고리의 다른 글

Try With Resource  (2) 2025.01.22
Record Class (불변 데이터 클래스)  (1) 2025.01.17
제네릭(Generic)  (0) 2025.01.17

* JDK14에서 preview로 등장하여 JDK16에서 정식 스펙으로 포함

불변(immutable) 객체를 쉽게 생성할 수 있도록 하는 유형의 클래스
	- 필드가 자동으로 final로 선언되어 불변 객체를 생성 (필드 캡슐화)
	- class가 final로 선언, final 클래스(상속불가)
	- 클래스의 필드 선언, 생성자, getter, equals, hashCode, toString이 모두 자동으로 생성 
	- 컴파일 타임에 컴파일러가 코드를 추가
	- getter를 사용할 때, getFieldName()이 아니라 fieldName()을 사용
	- Record 클래스가 제공해주는 메소드들은 재정의가 가능

1. 생성자는 모든 field를 포함
2. toString()도 모든 field를 포함
3. equals(), hashCode() 메서드는 invokedynamic based mechanism을 사용
4. getter는 field이름과 유사한 이름으로 생성 ex) id(), email()..
5. 기본적으로 java.lang.Record class를 상속받기 때문에 다른 class를 상속받을 수 없음
6. class가 final이기 때문에 다른 subclass를 생성할 수 없음
7. 모든 field는 불변이기 때문에 setter는 제공하지 않음
8. DTO(data transfer object)나 domain model class에 사용

Q. 레코드(record)를 JPA의 Entity 클래스로 사용할 수 없을까요?
레코드는 바이트코드로 본 것처럼 final 클래스(상속불가)이고, abstract로 선언할 수 없습니다.
따라서 레코드를 Entity 클래스에 사용할 수 없는데요, 그 이유는 JPA의 지연로딩에 있습니다.

지연 로딩 방식을 사용할 때, JPA는 엔티티 객체의 프록시 객체를 생성합니다. 
프록시 객체는 원본 객체를 상속하여 생성된 확장 클래스입니다. 
하지만 레코드는 상속이 불가능하므로 엔티티로 사용할 수 없습니다.

 

참고

 

예시 ResponseMessage<T>.java

package org.example.exception;

import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

@Getter
@Builder
@RequiredArgsConstructor
public class ResponseMessage<T> {
    private final HttpStatus status;
    private final String message;
    private final T data;

    public static ResponseEntity<ResponseMessage> error(CustomException e) {
        return ResponseEntity
                .status(e.getCustomErrorCode().getHttpStatus())
                .body(ResponseMessage.builder()
                        .status(e.getCustomErrorCode().getHttpStatus())
                        .message(e.getCustomErrorCode().getMessage())
                        .build());
    }

    public static <T> ResponseEntity<ResponseMessage> success(HttpStatus status, String message, T data) {
        return ResponseEntity
                .status(status)
                .body(ResponseMessage.builder()
                        .status(status)
                        .message(message)
                        .data(data)
                        .build());
    }
}

 

✏️ Class to Record 

package org.example.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

public record ResponseMessage <T> (HttpStatus status, String message, T data) {

    public static ResponseEntity<ResponseMessage> error(CustomException e) {
        return ResponseEntity
                .status(e.getCustomErrorCode().getHttpStatus())
                .body(new ResponseMessage<>(
                        e.getCustomErrorCode().getHttpStatus(),
                        e.getCustomErrorCode().getMessage(),
                        null
                ));
    }

    public static <T> ResponseEntity<ResponseMessage> success(HttpStatus status, String message, T data) {
        return ResponseEntity
                .status(status)
                .body(new ResponseMessage<>(
                        status,
                        message,
                        data
                ));
    }
}

'Java' 카테고리의 다른 글

Try With Resource  (2) 2025.01.22
불변 객체(Immutable Object)  (0) 2025.01.18
제네릭(Generic)  (0) 2025.01.17

* 모듈(Module) : 독립적으로 배포될 수 있는 코드의 단위

멀티 모듈(Multi-Module) : 코드 뭉치를 하나의 프로젝트 안에서 관리하는것

모듈별 책임을 분리하고 유지보수성과 확장성을 확보하기 위해 멀티모듈을 도입

멀티모듈의 설계 / 구조는 계층 기반으로 구성
하나의 프로젝트를 여러 개의 모듈로 나누어 관리
최상위 프로젝트(root)와  여러 개의 하위 모듈(module)로 구성

project-root/
├── module-api/
│   ├── controller/
│   ├── dto/
│   ├── resources/
│   └── build.gradle
├── module-service/
│   ├── service/
│   ├── utils/
│   └── build.gradle
├── module-domain/
│   ├── entity/
│   ├── repository/
│   └── build.gradle
├── module-core/
│   ├── conf/
│   ├── utils/
│   └── build.gradle
└── build.gradle (루트 설정)

project-root: 전체 프로젝트를 대표하는 루트 프로젝트로, 공통적인 빌드 설정을 관리
module-*: 각 모듈은 서로 다른 기능을 담당
	module-api: 컨트롤러 및 외부와의 통신을 처리
	module-service: 비즈니스 로직과 서비스 계층을 관리
	module-domain: 데이터 모델 및 데이터 접근 계층을 관리
	module-core: 공통 모듈로, 여러 모듈에서 재사용 가능한 유틸리티 및 설정 포함

 

멀티모듈의 주요 특징

1.	모듈화로 인해 코드 분리가 명확함
		각 모듈은 특정한 책임만을 가지며 서로 독립적
		결합도를 낮추고 응집도를 높이는 설계가 가능
2.	재사용성
		공통적인 로직이나 유틸리티를 module-core 같은 공통 모듈에 작성하여, 다른 모듈에서 재사용 가능
3.	빌드 및 배포 효율성
		변경 사항이 있는 모듈만 빌드하여 배포할 수 있어 빌드 시간을 단축
		Gradle이나 Maven을 사용하면 하위 모듈의 의존성과 빌드 순서를 관리 가능
4.	팀 협업 효율성
		각 팀이 서로 다른 모듈을 개발하도록 나누어 작업 가능
		충돌을 최소화하고 생산성 증가

 

멀티모듈 프로젝트의 장점

유지보수성: 각 모듈이 독립적이므로 변경 사항의 영향 범위가 작음
확장성: 새로운 기능을 추가할 때, 기존 모듈에 영향을 주지 않고 새로운 모듈로 추가 가능
재사용성: 특정 모듈을 여러 프로젝트에서 재사용 가능
의존성 관리: 각 모듈이 개별적으로 의존성을 관리하므로 필요하지 않은 의존성을 배제할 수 있음

 

경험한 어려움 및 해결 방안

경험한 어려움 및 해결 방안 
1: 의존성 관리의 어려움
module-service에서도 @Transactional을 사용하려면 spring-boot-starter-data-jpa가 필요
해결 : JPA 의존성을 추가

2. 멀티모듈 간 의존성 순환 문제 
module-core는 최대한 독립적으로 설계하고 PoJo 기반 순수 자바 클래스로 구성
단방향 의존성을 유지하여 의존성 순환 방지 

3. 멀티모듈에서 공통 설정 (Config 파일) 관리
공통 모듈의 역할을 명확하게 정리 → module-core가 비대해지지 않도록 주의

*. Config 위치
	api 로 가는 경우
		- WebMvcConfig: API 요청/응답 설정 (예: 메시지 컨버터, CORS 등)
		- SwaggerConfig: Swagger 문서화 설정
		- SecurityConfig: API 인증/인가 관련 설정 (Spring Security, OAuth)
		- RestTemplateConfig: 외부 API 호출을 위한 RestTemplate 설정
	core 로 가는 경우 
		- LoggingConfig: 로깅 설정
		- GlobalExceptionHandler: 전역 예외 처리
		- CommonConstants: 프로젝트 전반에서 사용하는 상수들
		- EncryptionConfig: 암호화 관련 공통 설정
  그 외 
		- feign : service 또는 (특정 API 모듈에서만 사용된다면) api 
		- Redis  : 	캐싱 또는 비즈니스 로직과 관련(service), 또는 core
		- JPA Config (Audit 등) : domain

 

 


 

멀티 모듈 적용

1. 모듈단위로 잘게 쪼개서 사용하고 싶어서 사용 (재활용) 

2. api / consumer(kafka) / batch  > service > domain > common
	- 엔트리포인트(=접근하는 컨트롤러) 부터 시작
    - core(=common)에는 어떤 엔트리포인트에도 문제없는(=특정기술에 종속 X) 순수한 포조객체 , Utils 메소드
    - domain에는 POJO(plain old Java Object), pojo + entity = jpa entity
    - 항상 단방향 ex. service 모듈 에서 common implement

 

멀티 모듈 회고

1. Api 모듈
	- api 모듈은 프로젝트의 엔트리 포인트
	- 외부로부터의 요청을 처리하는 컨트롤러와 데이터 전달을 위한 객체인 DTO(Data Transfer Object) 포함
    - 그 외 AOP, 공통 설정, 예외 처리 등을 포함 
    - 최상위 계층으로 외부와의 직접적인 인터페이스 역할을 하며, 모든 하위 모듈을 참조할 수 있는 구조로 설계
    - API의 단순히 요청을 라우팅하고 데이터를 가공하는 역할에 집중해야 한다고 생각
    - 그 외 추가적으로 배치, 컨슈머와 같은 다른 엔트리 포인트와도 구조적으로 통일성을 유지

2. Service 
	- 핵심 비즈니스 로직을 처리하고 데이터의 흐름을 제어하는 중간 계층
    - Domain 모듈과 협력하여 데이터 조작 및 검증을 처리하며, API 모듈로부터 요청을 전달받아 로직을 수행
    - 단방향 의존성을 유지하면서 Core 및 Domain 모듈만 참조
    - VO(Value Object) 비즈니스 로직과 관련된 값 객체를 포함 


3. Domain 모듈
	- 프로젝트의 핵심 데이터를 정의하고, 데이터 접근 계층을 처리하는 중요한 역할
	- POJO 기반의 엔티티와 JPA를 활용한 레포지토리 계층으로 구성
    - 특정 비즈니스 로직이 없는 순수한 데이터 계층으로 유지
    - module-core만 참조하도록 설계하여, 특정 비즈니스 로직이 없는 순수한 데이터 계층으로 유지
    - 다른 모듈에서 도메인 객체를 재사용할 수 있도록 돕고, 데이터 계층과 비즈니스 로직 간의 결합도를 낮춤
    - JPA 엔티티와 레포지토리를 포함하여 특정 기술 스택(JPA)에 종속적일 수 있음

4. Core 모듈
	- 공통적으로 사용되는 유틸리티와 설정을 모아둔 가장 하위 계층
    - 특정 기술 스택에 종속되지 않고, 
    배치 작업이나 다른 엔트리 포인트에서도 독립적으로 활용될 수 있도록 POJO 기반의 순수한 자바 클래스로 구성
    - 모든 모듈에서 재사용 가능한 코드를 포함하며, JPA와 같은 기술 의존성을 철저히 배제
    - 기술 변경이나 새로운 기능 추가 시에도 유연하게 대응할 수 있도록 설계
    - ex. 공통 예외 처리나 유틸 메서드처럼 어디서나 필요한 기능

5. Config 위치
	api 로 가는 경우
		- WebMvcConfig: API 요청/응답 설정 (예: 메시지 컨버터, CORS 등)
		- SwaggerConfig: Swagger 문서화 설정
		- SecurityConfig: API 인증/인가 관련 설정 (Spring Security, OAuth)
		- RestTemplateConfig: 외부 API 호출을 위한 RestTemplate 설정
	core 로 가는 경우 
		- LoggingConfig: 로깅 설정
		- GlobalExceptionHandler: 전역 예외 처리
		- CommonConstants: 프로젝트 전반에서 사용하는 상수들
		- EncryptionConfig: 암호화 관련 공통 설정

5. 그 외 
	feign : service 또는 (특정 API 모듈에서만 사용된다면) api 
	Redis  : 	캐싱 또는 비즈니스 로직과 관련(service), 또는 core
	JPA Config (Audit 등) : domain

 

참고 : 

https://www.youtube.com/watch?v=ipDzLJK-7Kc

https://www.youtube.com/watch?v=uvG-amw2u2s

https://www.youtube.com/watch?v=nH382BcycHc

https://techblog.woowahan.com/2637/

https://jojoldu.tistory.com/123

https://mangkyu.tistory.com/304

 

더보기

# 전체 빌드 (test 제외)

./gradlew build --stacktrace --info -x test

# 특정 모듈 빌드 # main 없는 경우 (common, domain, infra)

./gradlew :{project-name}-domain:build

# main 있는 경우 (api)

./gradlew :{project-name}-domain:bootJar

 

bootJar : dependecies와 클래스 파일을 모두 묶어서 빌드

jar : 클래스만 묶어서 빌드

 

 

 

'Spring' 카테고리의 다른 글

디버깅 모드  (0) 2025.01.19
FeignClient  (1) 2025.01.19
API(application programming interface), RestAPI  (0) 2023.05.22
웹 동작방식, HTTP, WAS, Web Server  (0) 2023.05.21
SOLID 원칙  (0) 2023.04.07

제네릭이란 ? 

자바(Java)와 같은 프로그래밍 언어에서 코드의 재사용성과 타입 안전성을 높이기 위해 사용하는 템플릿

데이터 형식에 의존하지 않고, 하나의 값이 여러 다른 데이터 타입들을 가질 수 있도록 한다

컴파일 시점에 데이터 타입을 명확히 지정할 수 있어, 코드의 유연성안전성을 동시에 확보

 

 

제네릭의 주요 특징 

1. 타입 안정성(Type Safety)

제네릭을 사용하면 컴파일 시점에 타입이 결정되므로, 잘못된 타입 사용으로 인한 오류를 방지

예: List<String>은 문자열만 저장 가능하며, 컴파일 시점에 다른 타입을 넣으려 하면 에러가 발생

2. 코드 재사용성

제네릭을 사용하면 다양한 데이터 타입에 대해 하나의 코드로 처리 가능 

예: List<T>는 문자열, 숫자, 사용자 정의 객체 등 어떤 타입이든 처리

3. 형변환(Downcasting) 제거

제네릭을 사용하면 형변환(casting)이 필요 없어 코드가 간결하고 안전

예: List<String>에서 값을 꺼낼 때는 문자열(String)로 자동 처리

 

제네릭의 사용법

1. 클래스에서 제네릭 사용

// 제네릭 클래스를 정의
public class Box<T> { 
    private T value;

    public void setValue(T value) { 
        this.value = value; 
    }
    
    public T getValue() { 
        return value; 
    }
}

// 사용하는 예제
Box<String> stringBox = new Box<>();
stringBox.setValue("Hello");
System.out.println(stringBox.getValue()); // 출력: Hello

Box<Integer> intBox = new Box<>();
intBox.setValue(123);
System.out.println(intBox.getValue()); // 출력: 123

2. 메서드에서 제네릭 사용

// 제네릭 메서드
public static <T> void printArray(T[] array) {
    for (T element : array) {
        System.out.println(element);
    }
}

// 사용하는 예제
String[] stringArray = { "A", "B", "C" };
printArray(stringArray);

Integer[] intArray = { 1, 2, 3 };
printArray(intArray);

3. 제네릭 인터페이스

// 제네릭 인터페이스 정의
public interface Repository<T> {
    void save(T entity);
    T findById(int id);
}

// 구현 클래스
public class UserRepository implements Repository<User> {
    @Override
    public void save(User user) {
        System.out.println("Saving user: " + user.getName());
    }
    
    @Override
    public User findById(int id) {
        return new User(id, "Sample User");
    }
}

 

참고: 

https://inpa.tistory.com/entry/JAVA-☕-제네릭Generics-개념-문법-정복하기

https://st-lab.tistory.com/153

'Java' 카테고리의 다른 글

Try With Resource  (2) 2025.01.22
불변 객체(Immutable Object)  (0) 2025.01.18
Record Class (불변 데이터 클래스)  (1) 2025.01.17
더보기

복제란 네트워크로 연결된 여러 장비에 동일한 데이터의 복사본을 유지한다는 의미 

 

데이터의 복제가 필요한 이유 

  • 지리적으로 사용자와 가깝게 데이터를 유지해 지연 시간을 줄인다.
  • 일부 노드에 장애가 발생해도 동작할 수 있도록 해 가용성을 높인다.
  • 읽기 질의를 제공하는 노드의 수를 확장해 읽기 처리량을 늘린다.

변경되는 데이터를 노드 간 복제하기 위한 대표적인 알고리즘

  • 단일 리더
  • 다중 리더
  • 리더 없는복제 방식

1. 리더와 팔로워

DB의 복사본을 저장하는 각 노드 : 복제 서버(replica)

모든 복제 서버에 데이터가 복제된 것을 보장하기 위해 일반적으로 리더 기반 복제를 활용

  • 복제 서버 중 하나를 리더(마스터, 프라이머리)로 지정 클라이언트로부터의 모든 쓰기 요청은 리더에게만 전달
  • 리더는 자신의 서버에 데이터를 기록하고 팔로워(슬레이브, 세컨더리) 복제 서버에 쓰기 요청을 전달
    • 클라이언트 관점에서 쓰기는 리더만 허용하므로 팔로워는 읽기 전용
    • 리더가 팔로워에게 쓰기 내용을 전달할 땐 복제 로그(replication log)나 변경 스트림(change stream)을 주로 활용
  • 리더기반 복제 모드는 여러 RDB에 내장되어 있고 분산 메시지 브로커에서도 사용

1-1. 동기식 대 비동기식 복제 

  • 동기식 복제의 장단점
    • 장점: 팔로워가 리더와 일관성 있게 최신 데이터 복사본을 가지는 것을 보장
    • 단점: 팔로워가 죽거나 네트워크 문제로 동기 팔로워가 응답하지 않을 경우 쓰기가 처리될 수 없음
  • 임의 한 노드의 장애가 전체 시스템을 멈추게 하므로 모든 팔로워가 동기식인 상황은 비현실적
  • 반동기식(semi-synchronouse)
    • 현실적으로 팔로워 하나는 동기식으로, 나머지는 비동기식으로 복제하는 방식을 사용
    • 적어도 두노드 (리더와 하나의 동기 팔로워)에 최신 복사본이 있는 것을 보장
  • 비동기식 복제의 장단점
    • 장점: 모든 팔로워가 잘못되더라도 리더가 쓰기 처리를 계속 할 수 있음 
    • 단점: 리더가 잘못되고 복구 불가능한 상황에서 팔로워에 아직 복제되지 않은 모든 쓰기는 유실 (지속성 보장 X)
  • 비동기식 복제는 내구성을 약화시키지만 많은 팔로워가 있거나 지리적으로 분산되었다면 비동기식 복제를 사용

1-2.  새로운 팔로워 추가 

  • 팔로워가 추가되어 기존 리더의 데이터를 복제할 때 가용성을 위해 중단없이 수행가능해야 한다. 그 과정을 다음과 같다.
    1. 전체 DB를 잠그지 않고, 리더 DB의 스냅샷을 일정시점에 가져온다.
    2. 스냅숏을 새로운 팔로워 노드에 복사한다.
    3. 팔로워는 리더에 연결해 스냅샷 이후 발생한 모든 데이터 변경을 요청한다
    4. 팔로워가 스냅샷 이후 데이터 변경의 미처리분(backlog)을 모두 처리하면 따라잡았다고 말한다.

1-3. 노드 중단 처리

리더 기반 복제에서 고가용성 달성하는 방법

팔로워 장애 : 따라잡기 복구

각 팔로워는 리더로부터 수신한 데이터 변경 로그를 로컬 디스크에 보관,  팔로워 장애는 쉽게 복구 가능

  • 보관된 로그에서 결함이 발생하기 전에 처리한 마지막 트랜잭션을 확인
  • 팔로워는 리더에 연결해 끊어진 동안 발생한 데이터 변경을 모두 요청

리더 장애 : 장애 복구

  • 장애 복구(failover)
    • 팔로워 중 하나를 새로운 리더로 승격해야 하고, 클라이언트는 새로운 리더로 쓰기를 전송하기 위해 재설정이 필요
    • 다른 팔로워는 새로운 리더로부터 데이터 변경을 소비하기 시작
  • 참고. 장애 복구 단계
  • 일부 운영팀은 소프트웨어가 자동 장애 복구를 지원하더라도 수동으로 장애 복구를 수행하는 방식이 선호
  • 노드 장애, 불안정한 네트워크, 지속성, 가용성, 지연 시간 등의 문제는 분산 시스템에서 발생하는 근본적인 문제

1-4. 복제 로그 구현

구문 기반 복제

  • 리더는 모든 쓰기 요청을 기록하고 쓰기를 실행한 다음 구문 로그를 팔로워에게 전송
  • RDB는 INSERT, UPDATE, DELETE 구문을 팔로워에게 전달하고 각 팔로워는 SQL 구문을 파싱하고 실행
  • 복제가 깨질 수 있는 다양한 사례
  • 대안 해결책
    • 리더가 구문을 기록할때 비결정적 함수의 고정 값을 반환하게끔 대처 
    • MySQL 5.1 이후 구문에 비결정성이 있는 경우 로우 기반 복제를 사용하도록 변경

쓰기 전 로그 배송

  • 일반적으로 저장소 엔진은 모든 쓰기에 대한 로그 기록
  • 리더는 디스크에 로그를 기록하는 일 외에도 팔로워에게 네트워크로 해당 로그를 전송
  • 가장 큰 단점은 로그가 제일 저수준의 데이터를 기술
  • 복제가 저장소 엔진과 밀접하게 결합되어 리더와 팔로워의 데이터베이스 소프트웨어 버전을 다르게 실행 할 수 없음

논리적(로우 기반) 로그 복제 

  • 복제 로그를 저장소 엔진 내부와 분리하기 위한 대안 하나는 복제와 저장소 엔진을 위해 다른 로그 형식을 사용하는 것
  • 논리적 로그(lgical log) : 저장소 엔진의 (물리적) 데이터 표현과 구별하기 위한 복제 로그 
    • RDB용 논리적 로그는 대개 로우 단위로 DB 테이블에 쓰기를 기술한 레코드 열
    • 여러 로우를 수정하는 트랜젝션은 여러 로그 레코드를 생성한 다음 트랜젝션이 커밋 되었음을 레코드에 표시
  • 논리적 로그를 저장소 엔진 내부와 분리했으므로 하위 호환성을 더 쉽게 유지 가능
  • 리더와 팔로워에서 다른 버전의 데이터베이스 소프트웨어나 저장소 엔진을 실행 가능

트리거 기반 복제

  • 데이터의 서브셋만 복제하거나 데이터베이스를 다른 종류의 데이터베이스로 복제해야 하거나 충돌해소로직이 필요 한 경우
    • 복제를 애플리케이션 층으로 옮김 
  • 트리거는 사용자 정의 애플리케이션 코드를 등록가능 
    • 데이터베이스 시스템에서 데이터 변경 시(쓰기 트랜젝션) 자동으로 실행
  • 일반적으로 트리거 기반 복제는 다른 방식에 비해 오버헤드가 있지만 유연성 때문에 유용한 매우 유용

2. 복제 지연 문제

최종적 일관성(Eventual Consistency) : 데이터베이스에 쓰기를 멈추고 잠시동안 기다리면 팔로워는 결국 따라잡고 리더와 일치

 

2-1. 자신이 쓴 내용 읽기

  • 자신이 쓴 내용을 바로 다시 읽기 했을 때 복제 서버엔 쓰기가 반영되지 않아 쓰기 전 데이터를 볼 가능성이 존재
  • 쓰기 후 읽기 일관성(자신의 쓰기 읽기 일관성) 은 항상 자신이 갱신한 내용에 대해서는 일관성을 보장
    • 단, 다른 사용자에 대해서는 일관성을 보장하지 않음
  • (참고) 쓰기 후 읽기 일관성 구현방법

2-2. 단조 읽기

  • 사용자가 시간이 거꾸로 흐르는 현상을 목격할 수 있음
    • 복제 서버 A, B 중 A서버에만 동기화가 되어있는 시점에 사용자가 처음엔 A 서버를 통해 데이터를 읽을땐 데이터가 반환되지만 그 다음에 B 서버를 통해 데이터를 읽으면 데이터가 반환되지 않음
  • 단조 읽기(monotonic read)는 이런 종류의 이상 현상이 발생하지 않음을 보장
    • 각 사용자의 읽기가 항상 동일한 복제 서버에서 수행되게끔 함 (다른 사용자는 다른 복제 서버에서 읽을 수 있음)

2-3. 일관된 순서로 읽기

  • 데이터가 서로 다른 파티션에 저장되고 파티션 리더가 다를 때 실제 시간상으론 A -> B 순으로 입력되었지만 팔로워들은 B -> A 순으로 복제가 될 가능성
  • Observer는 실제로 쓰여진 순서가 아닌 반대로 읽는다
  • 일관된 순서로 읽기(Consistent Prefix Read)는 이런 종류의 이상 현상이 발생하지 않음을 보장
    • 특정 순서로 쓰기가 발생하면 이 쓰기를 읽는 모든 사용자는 같은 순서로 쓰여진 내용을 보게 됨을 보장

2-4. 복제 지연을 위한 해결책 

  • 최종적 일관성으로 인한 복제 지연이 애플리케이션에 얼마나 영향을 끼치는지 파악할 필요가 있다
  • 지연에 대한 영향이 크다면 강력한 일관성을 보장할 수 있도록 시스템을 설계해야 한다
  •  애플리케이션에서 강력한 보장을 제공할 수도 있지만 애플리케이션에서 다루기엔 복잡하다
  • 트랜잭션은 이러한 문제에 대한 해결을 데이터베이스 단에서 보장해주지만 
  • 분산 데이터베이스로 전환되면서 많은 시스템이 트랜잭션을 포기했다
  • 트랜잭션이 성능과 가용성 측면에서 너무 비싸고, 확장 가능한 시스템에서는 어쩔 수 없이 최종적 일관성을 사용해야한다는 주장이 존재.

2. 다중 리더 복제

다중리더 설정 : 쓰기를 허용하는 노드를 하나 이상 두는 것으로 자연스럽게 확장

 

2-1. 다중 리더 복제의 사용 사례

1) 다중 데이터센터 운영

  • 각 데이터센터마다 리더가 존재하는 방식으로 다중 리더를 세팅
  • 데이터센터간에는 각 데이터센터의 리더가 다른 데이터센터의 리더에게 변경사항을 복제
  • 다중 리더 복제의 단점 : 동일한 데이터를 다른 두 개의 데이터 센터에서 변경할 경우 쓰기 충돌 해소 필요

2) 오프라인 작업을 하는 클라이언트

  • 모든 디바이스에는 리더처럼 동작하는 로컬 데이터베이스가 존재 (쓰기 요청을 받아야 함)
  • 디바이스의 인터넷이 연결되면 로컬에 변경된 데이터가 복제 서버로 동기화 된다.
  • 아키텍처 관점에서 보면 이 설정은 근본적으로 데이터 센터 간 다중 리더 복제와 동일하다.

2-2. 쓰기 충돌 다루기 

  • 다중 리더 복제의 가장 큰 문제 : 쓰기 충돌
    • 각 사용자의 변경을 로컬 리더에 적용하고 변경을 비동기로 복제시 출동을 감지
  1. 충돌 회피 : 충돌을 처리하는 제일 간단한 전략
  2. 일관된 상태 수렴 : 모든 복제 계획은 모든 복제 서버가 최종적으로는 동일하다라는 사실을 보장해야 한다 
  3. 사용자 정의 충돌 해소 로직 : 전체 트랜젝션이 아닌 개별 로우나 문서 수준에서 적용된다

2-3. 다중 리더 복제 토폴로지

  • 복제 토폴로지는 쓰기를 한 노드에서 다른 노드로 전달하는 통신 경로를 말한다.
  • 리더가 둘 이상이라면 다양한 토폴로지가 가능하다.
    • 전체 연결 토폴로지는 모든 리더가 각자의 쓰기를 다른 모든 리더에서 전송한다
    • 원형 토폴로지는 각 노드가 하나의 노드로부터 쓰기를 받고, 이 쓰기를 다른 한 노드에 전달한다.
    • 별 모양 토폴로지는 지정된 루트 노드 하나가 다른 모든 노드에 쓰기를 전달한다. 트리로 일반화된다.

3. 리더가 없는 복제 

일부 데이터 저장소 시스템은 리더의 개념을 버리고 모든 복제 서버가 클라이언트로부터 쓰기를 직접 받을 수 있게 허용하는 접근 방식을 사용하기도 한다.

리더 없는 복제는 아마존이 내부 다이나모 시스템에서 사용한 후 다시 데이터베이스 아키텍처로 유행했다.

리악, 카산드라, 볼드모트는 다이나모에서 영감을 얻은 리더 없는 복제 모델의 오픈소스 데이터스토어이며 이런 종류의 데이터베이스를 다이나모 스타일이라고 부른다.

3-1. 노드가 다운됐을 때 데이터 베이스에 쓰기

  • 읽기 복구와 안티 엔트로피
    • 복제 계획은 최종적으로 모든 데이터가 모든 복제 서버에 복사됨을 보장해야 한다.
    • 다이나모 스타일은 누락된 쓰기 처리를 위해 두 가지 메커니즘을 주로 사용한다.
      • 읽기 복구
      • 안티 엔트로피 처리
    • 읽기 복구만 사용하는 경우 값을 읽을 때만 복구가 가능하므로 읽지 않는 값은 일부 복제본에 누락될 가능성이 존재한다.
  • 읽기와 쓰기를 위한 정족수
    • n개의 복제 서버가 있을 때 w개의 노드에 쓰기 성공이 보장되고 읽기는 r개 노드로 수행되는 경우에 w + r > n의 경우 항상 최신 값을 읽는 것을 기대한다.
    • 정족수(Quorum) 읽기와 쓰기인 경우 위 식을 보장한다. 

3-2. 정족수 일관성의 한계

  • 모든 읽기가 항상 최신값을 반환함을 보장하는 경우 흔히 Strong Consistency(강력한 일광성)을 보장한다고 한다.
  • 보통 w + r > n인 경우 강력한 일관성이 보장된다고 하지만 정족수 읽기와 쓰기인 경우 강력한 일관성을 보장하지 않는 에지 케이스가 존재한다.
  • 최신성 모니터링
    • 리더 기반 복제에서 데이터베이스는 일반적으로 복제 지연에 대한 지표를 노출한다 
    • 리더없는 복제 서비스에서 복제 서버의 오래됨을 측정하고 매개변수에 따라 오래된 값을 읽는 비율을 예측하는 연구가 있었다
    • 최종적 일관성은 의도적으로 모호한 보장이지만 운용성을 위해서는 최종적 을 정량화 할 수 있어야 한다

3-3. 느슨한 정족수와 암시된 핸드오프

  • 적절히 설정된 정족수가 있는 데이터베이스는 장애 복구 없이 개별 노드 장애를 용인한다
  • 노드가 n개 이상인 대규모 클러스터에서 특정 값을 위한 정족수가 충족되지 못하더라도 n에 포함되지 않은 다른 노드에 우선적으로 쓰기를 기록하는 방식을 느슨한 정족수라고 부른다
  • 느슨한 정족수는 쓰기 가용성을 높이는데 유용하지만 w + r > n인 경우에도 강력한 일관성을 보장하지 않는다
  • 느스한 정족수는 모든 일반적인 다이나모 수현에서 선택 사항이다

3-4. 동시 쓰기 감지

  • 다이나모 스타일 데이터베이스는 여러 클라이언트가 동시에 같은 키에 쓰는 것을 허용하므로 엄격한 정족수를 사용하더라도 충돌이 발생한다.
  • 동시 쓰기가 발생했을 때 네트워크 지연이나 부분적인 장애로 인해 쓰기 요청이 다른 노드에 다른 순서로 도착하여 일관성이 깨질 수 있다.
  • 최종적 일관성을 달성하기 위해 복제본들은 동일한 값이 돼야 한다
  • 쓰기 충돌 해소를 위한 기법
    • 최종 쓰기 승리(동시 쓰기 버리기)
      • 예전 값은 버리고 최신 값으로 덮어쓰는 방법이다. 
      • 어떤 쓰기가 최신인지 명확하게 결정할 수 있는 한 최종적 일관성이 보장된다.
      • 카산드라에서 유일하게 제공하는 충돌 해소 방법이고 리악에서는 선택적 기능이다.
      • LWW로 데이터베이스를 안전하게 사용하는 유일한 방법은 키를 한번만 쓰고 이후에는 불변 값으로 다루는 것이다.
    • 이전 발생 관계와 동시성 
      • 사실 작업이 다른 작업보다 먼저 발생하지 않으면 단순히 동시 작업이라 말한다
    • 동시에 쓴 값 병합
      • 형제 : 여러 작업이 동시에 발생한 경우 클라이언트가 합쳐 정리한 동시에 쓴 값 
      • 서버는 모든 키에 대해 버전을 기록하고 클라이언트가 키를 읽을 때 최신 버전뿐만 아니라 덮어쓰지 않은 모든 값을 병합하여 반환한다
      • 톰스톤 : 버전 값들을 병합할 때 상품이 제거되었음을 알 수 있게 남겨 둔 삭제 표시

참고

'Books' 카테고리의 다른 글

[대규모 중심 어플케이션 설계] 07장. 트랜잭션  (0) 2025.01.23

가상화 환경 또는 가상 머신 (Virtual Machine, VM)은 실제 하드웨어 시스템을 에뮬레이션하거나 복제한 소프트웨어 구현체입니다. 이는 다른 운영 체제 (OS)를 호스트 시스템에서 독립적으로 실행할 수 있게 해주며, 하나의 물리적 기계에서 여러 가상 시스템을 구동하는 것이 가능하게 합니다. 가상화 기술은 다양한 용도로 사용될 수 있습니다:

  • 개발 및 테스트: 개발자들은 가상 머신을 사용하여 다양한 OS와 환경에서 소프트웨어를 테스트하고 디버깅할 수 있습니다. 이는 개발 단계에서 버그를 찾고 수정하는 데 도움이 됩니다.
  • 서버 통합: 가상화를 통해 하나의 물리적 서버에서 여러 가상 서버를 운영함으로써 하드웨어 사용을 최적화하고 운영 비용을 절감할 수 있습니다.
  • 레거시 시스템 실행: 일부 애플리케이션은 특정 OS나 환경에서만 작동합니다. 가상화를 통해 이러한 레거시 애플리케이션을 최신 시스템에서 실행할 수 있습니다.
  • 보안 및 안전성 : 가상화는 시스템 간의 격리를 제공하므로, 하나의 시스템에서 문제가 발생하더라도 다른 시스템에 영향을 미치지 않습니다. 이는 보안 및 안정성을 향상시키는 데 도움이 됩니다.

가상화 환경을 관리하고 생성하는 데는 여러 소프트웨어 도구가 있습니다. VMware, VirtualBox, Hyper-V, KVM 등이 있습니다. 이들은 사용자에게 CPU, 메모리, 디스크 공간, 네트워크 인터페이스 등의 가상 자원을 제공하며, 각기 다른 운영 체제를 병렬로 실행할 있게 해줍니다.

 

 

쿠버네티스를 왜 해야 하나?

: 개발을 하게 될때 어플리케이션을 만들고 배포 과정을 도와주는 플랫폼으로 쿠버네티스를 많이 사용하고 있기 때문에

 

쿠버네티스란 애플리케이션을 개발하고 배포하는 과정을 크게 단순화해주는 플랫폼이다. 

 

미들웨어에서의 컨테이너 개념은 일반적으로 컨테이너 기반의 가상화 기술을 의미합니다. 컨테이너는 소프트웨어를 실행하는데 필요한 모든 요소(코드, 런타임, 시스템 도구, 시스템 라이브러리 등)를 패키지화하여 일관된 환경에서 소프트웨어를 실행하는 기술입니다.

미들웨어에서 컨테이너 기술을 이용하면, 애플리케이션과 그에 필요한 미들웨어 컴포넌트들(웹 서버, 메시지 큐, 데이터베이스 등)을 독립적으로 패키지화하고, 배포하며, 실행하고, 관리할 수 있습니다. 이는 개발, 테스트, 배포의 과정을 간소화하고, 환경에 따른 문제를 최소화하는 등의 이점을 제공합니다.

 

2023.06.28 - [Infra] - 미들웨어(Middleware), IaaS(Infrastructure as a Service), PaaS(Platform as a Service), SaaS(Software as a Service)

 

더보기

쿠버네티스(Kubernetes)는 컨테이너화된 애플리케이션을 배포, 확장 및 관리하기 위한 오픈소스 플랫폼으로, 그 자체로는 PaaS(Platform as a Service)를 완전히 제공하지는 않습니다. 쿠버네티스는 보다 기본적인 수준에서 동작하는 시스템으로, 기본적인 빌딩 블록(컨테이너, 서비스, 볼륨 등)을 제공하고, 이를 통해 사용자가 원하는 플랫폼을 구축할 수 있게 해줍니다.

그러나 쿠버네티스 위에 추가적인 추상화 계층을 제공하는 오픈소스 프로젝트나 상용 제품들이 있으며, 이러한 솔루션들을 사용함으로써 쿠버네티스를 기반으로 하는 PaaS를 구축할 수 있습니다. 예를 들어, OpenShift, Rancher, Google Cloud Run 등이 이에 해당합니다.

결국, 쿠버네티스는 컨테이너 오케스트레이션을 위한 필수적인 기능을 제공하며, 이를 기반으로 사용자나 기업이 자신들의 요구사항에 맞는 PaaS 구축하는 사용될 있습니다. 이는 쿠버네티스가 제공하는 유연성과 확장성 때문에 가능한 것입니다.

 

컨테이너 관리 플랫폼인 Docker Kubernetes 이러한 컨테이너 기술을 대중화시키는 역할을 하였습니다.

 

Docker는 컨테이너화 된 애플리케이션을 생성하고 관리하기 위한 오픈 소스 플랫폼입니다. Docker는 컨테이너라는 개념을 대중화했으며, 컨테이너를 사용하여 소프트웨어 의존성 문제를 해결했습니다.

컨테이너는 코드, 런타임, 시스템 도구, 시스템 라이브러리 등 애플리케이션 실행에 필요한 모든 것을 포함하므로, 개발자는 코드가 예상대로 실행될 것임을 확신할 수 있습니다. Docker 컨테이너는 거의 모든 곳에서 실행될 수 있으므로, 소프트웨어 배포는 물론, 배포, 테스트, 그리고 디버그 과정도 단순화되었습니다.

Kubernetes (또는 줄여서 k8s 또는 kube)는 컨테이너화 된 애플리케이션을 자동으로 배포, 확장, 관리할 수 있는 오픈 소스 플랫폼입니다. Kubernetes는 컨테이너 오케스트레이션 시스템으로, 대규모의 컨테이너화 된 애플리케이션을 관리하고 운영하는데 필요한 도구를 제공합니다.

Kubernetes는 서비스 검색, 로드 밸런싱, 자동 복구, 스케일링, 롤링 업데이트, 시크릿과 구성 관리 등을 지원합니다. 이를 통해 사용자는 복잡한 머신 클러스터 위에서 애플리케이션을 보다 효율적으로 운영할 수 있습니다.

Docker Kubernetes 상호 보완적인 관계로, Docker 애플리케이션을 패키지화하고 실행하는데 필요한 플랫폼을 제공하며, Kubernetes 이러한 Docker 컨테이너를 대규모로 관리하고 운영하는데 필요한 도구를 제공합니다.

  • 자동 복구(Auto Healing): 쿠버네티스는 시스템에 장애가 발생하면 애플리케이션을 자동으로 복구하는 기능을 제공합니다. 예를 들어, 컨테이너가 실패하면, 노드가 다운되면, 또는 애플리케이션 자체가 사용자 정의 상태 체크를 통과하지 못하면 쿠버네티스는 문제가 된 컨테이너를 재시작하여 애플리케이션을 복구할 수 있습니다.
  • 스케일링과 로드 밸런싱(Scaling and Load Balancing): 쿠버네티스는 애플리케이션의 트래픽 요구사항에 따라 동적으로 스케일링할 수 있습니다. 또한, 부하 분산을 위한 서비스를 제공하여 애플리케이션의 트래픽을 여러 인스턴스로 분산시키는 기능도 제공합니다.
  • 코드 배포 및 롤백(Deployment and Rollback): 쿠버네티스를 사용하면 새로운 버전의 애플리케이션을 빠르고 안정적으로 배포할 수 있으며, 문제가 발생하면 이전 버전으로 쉽게 롤백할 수 있습니다.
  • 개발 운영 효율성(Dev and Ops Efficiency): 쿠버네티스는 애플리케이션을 개발하고 운영하는 과정에서의 효율성을 크게 향상시킵니다. 개발팀은 애플리케이션 코드에 집중할 있으며, 운영팀은 배포, 모니터링, 로깅 등의 운영 작업을 효과적으로 수행할 있습니다.
더보기
  • 컨테이너: 더 작고 더 빠르게 애플리케이션을 실행할 수 있게 해주는 소프트웨어 표준화 단위입니다. 이는 애플리케이션의 코드, 설정 및 종속성을 하나로 모아 각 환경에서 동일하게 실행할 수 있게 해줍니다. 컨테이너는 리눅스 커널 기술인 namespaces와 cgroups를 활용해 격리된 실행 환경을 제공합니다.
  • 쿠버네티스: 컨테이너화된 애플리케이션의 배포, 확장 및 관리를 자동화해주는 오픈 소스 플랫폼입니다. 여러 머신에 걸쳐 컨테이너가 동작하도록 분산 시스템을 구축하고 관리하는 것을 돕습니다.
  • 웹 서버와 컨테이너: 웹 서버는 일반적으로 운영 체제 위에 설치되며, 웹 브라우저와 같은 클라이언트의 HTTP 요청에 응답합니다. 반면, 컨테이너는 필요한 소프트웨어 및 라이브러리를 포함하여 웹 서버와 같은 애플리케이션을 격리된 환경에서 실행합니다. 이를 통해 개발 환경과 운영 환경 사이의 일관성을 보장하며, 배포 및 확장을 용이하게 합니다.
  • AWS와 EKS: AWS는 클라우드 컴퓨팅 서비스를 제공하는 플랫폼이며, 가상 머신을 구동하는 서비스인 EC2 등을 포함합니다. 반면, AWS EKS는 Amazon Elastic Kubernetes Service의 약자로, AWS에서 제공하는 완전 관리형 쿠버네티스 서비스입니다. EKS는 쿠버네티스를 사용하여 컨테이너화된 애플리케이션을 실행하고 관리할 수 있도록 해줍니다. EKS는 AWS의 EC2 인스턴스를 사용하여 쿠버네티스 클러스터를 구성할 수도 있습니다.

따라서 "쿠버네티스" 컨테이너를 관리하고 운영하는 플랫폼으로, 컨테이너화된 서버와 같은 애플리케이션을 확장 배포하는 도움이 됩니다

 

 

더보기

쿠버네티스 메인서버 3개쓰는 이유?

 

쿠버네티스(Kubernetes)에서 메인 서버를 일반적으로 "마스터 노드"라고 부릅니다. 마스터 노드는 클러스터의 작업을 조정하고 관리하는 중요한 역할을 담당합니다. 여기에는 API 서버, 스케줄러, etcd 등과 같은 중요한 구성 요소가 포함됩니다.

마스터 노드를 3개 사용하는 주된 이유는 고가용성(High Availability)과 내결함성(Fault Tolerance)을 달성하기 위해서입니다.

  • 고가용성: 클러스터에서 중요한 작업을 처리하는 마스터 노드가 다운되면 전체 시스템에 심각한 문제가 발생할 수 있습니다. 여러 개의 마스터 노드를 사용하면, 한 노드가 실패하더라도 다른 노드들이 역할을 이어받아 서비스 중단을 방지할 수 있습니다.
  • 내결함성: 또한, 여러 마스터 노드를 사용하면 노드 간에 상태 정보를 복제하여 장애가 발생했을 때 데이터 손실을 방지하고 빠르게 복구할 수 있습니다.

3개의 마스터 노드를 사용하는 것은 일반적인 선택이며, 이는 홀수 개의 노드를 통해 etcd(클러스터의 핵심 데이터를 저장하는 데이터베이스) 대한 분산 합의 알고리즘을 보다 효과적으로 실행할 있기 때문입니다. 3개를 사용하면 노드가 실패해도 나머지 노드가 정상적으로 작동하여 클러스터의 상태를 유지할 있습니다.