Cold Sequence

구독 할 때마다 타임라인이 새로 생성됨

구독하는 시점과 상관없이 첫번째로 emit 된 데이터부터 읽을 수 있음

ex) Flux.fromIterable(Array.asList("A", "B", "C")

subscribe(data -> Logger.info("data : {}", data) // ABC

subscribe(data -> Logger.info("data : {}", data) // ABC

 

 

Hot Sequence

타임라인이 하나만 존재

구독하는 시점에 따라 구독 시점 이후로 emit 된 데이터만 읽을 수 있음

ex) Flux.fromStream(Stream.of("A", "B", "C").delayElements(Duration.ofSeconds(1)).share()

subscribe(data -> Logger.info("data : {}", data) // A B

TimeUtils.sleep(2000)

subscribe(data -> Logger.info("data : {}", data) // C

* share() : cold 시퀀스를 hot 시퀀스로 변환해줌

반응형

'back > Reactive Java' 카테고리의 다른 글

Reactive 란?  (1) 2023.10.16

Reactive System 설계 원칙

아래 네가지 원칙을 잘 반영한 시스템

 

Responsive : 응답성 즉각적인 응답

Resilient : 회복성 장애시 회복성

Elastic : 탄력성 작업량 변화에도 응답성을 유지하는 것

Message Driven : 비동기(async) 메시지 주고받아 느슨한 결합(loose coupling), 위치 투명성(location transparency) 보장

 

 

Reactive programming 특징

1) 데이터 소스에 변경이 있을때마다 데이터를 전파 - publisher 데이터 전파, subscriber 전파된 데이터를 받아서 처리

2) 선언형 프로그래밍 패러다임 : 실행할 동작을 구체적으로 명시하지 않고 목표만 정의 

3) 함수형 프로그래밍 기법 사용

 

 

명령형 프로그래밍 vs 선언형 프로그래밍

명령형 프로그래밍

List<Integer> numbers = Arrays.asList(1,3,21,10,8);
int sum = 0;

for (int number: numbers) {
 if (number > 6 && (number % 2 != 0)) {
   sum += number;
 }
}

선언형 프로그래밍

List<Integer> numbers = Arrays.asList(1,3,21,10,8);

int sum = numbers.stream()
	.filter(number -> number > 6 && number%2!=0)
	.mapToInt(number -> number)
	.sum();

 

Reactive Streams 란?

리액티브 프로그래밍을 표준화 한 명세

https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.3/README.md#specification

 

reactive streams 4가지 인터페이스

1) Publisher : 데이터 통제

2) Subscriber : 퍼블리셔가 통제한 데이터를 구독하는 구독자

3) Subscription : 구독 자체를 정의

4) Process : Publisher & Subsriber 역할을 동시에 할 수 있는 인터페이스

 

 

Reactive Streams 구현체

1) RxJava

2) Java 9 Flow API

3) Akka Streams

4) Reactor

5) RxJS, RxScala, RxAndroid 등..

 

 

참고 :

reactivemanifesto.org

반응형

'back > Reactive Java' 카테고리의 다른 글

cold vs hot sequence  (1) 2023.11.06

서킷브레이커(CircuitBreaker)란

서킷브레이커 패턴이란 외부 서비스에 의한 문제를 방지하기 위해 등장한 디자인 패턴으로 문제가 발생한 지점을 감지하고 실패하는 요청을 계속하지 않도록 방지합니다.
그리고 이를 통해 시스템의 장애 확산을 막고 장애 복구를 도와주며 유저는 불필요하게 대기하지 않게 됩니다.
가정집에 있는 누전차단기가 화재를 막는 것과 비슷하게 CircuitBreaker(직역하면 회로차단기)는 서비스의 장애 전파를 막는다고 이해하면 됩니다.

아래 그림과 같이 ServiceA가 ServiceB를 호출 할 때
ServiceB가 반복적으로 실패한다면 CircuitBreaker 를 Open 하여 ServiceB에 대한 흐름을 차단하는게 서킷브레이커의 역할입니다.
* CircuitBreaker 의 Open 은 흐름을 차단하는 것으로, 흐름을 열어둔다(opened) 라는 의미가 아닙니다.
* 
반대로 CircuitBreaker 의 Closed 상태는 흐름을 허용하는 정상상태를 의미합니다.

이미지 출처 : https://symphony.is/blog/service-resiliency-with-spring-boot-and-resilience4j


"CircuitBreaker 가 무엇인지 그리고 어떤 역할을 하는지 알았으니, 이제 CircuitBreaker의 상태인 Closed/Open/Half Open에 대해 좀 더 알아보겠습니다."

 

서킷브레이커의 3가지 상태

  Closed Open HalfOpen
상황 정상 장애 Open 상태가 되고 일정 요청 횟수/시간이 지난 상황.
Open 으로 상태를 변경할지, Closed 로 상태를 변경할지에 대한 판단이 이루어지는 상황
요청에 대한 처리 요청에 대한 처리 수행.
정해진 횟수만큼 실패할 경우 Open 상태로 변경
외부 요청을 차단하고 에러를 뱉거나 지정한 callback 메소드를 호출 요청에 대한 처리를 수행하고 실패시 CircuitBreaker 를 Open 상태로 변경.
성공시 CircuitBreaker를 Close 상태로 변경

* 서킷브레이커에서 장애판단의 기준(Closed 상태에서 Open 이 되기 위해 카운팅 되는 실패의 기준)은 아래와 같습니다.

1) slow call : 기준보다 오래 걸린 요청

2) failure call : 실패하거나 오류 응답을 받은 요청

* slow call 과 failure call 은 CircuitBreaker의 프로퍼티로 정의되어 있으며 사용자가 특정 값으로 지정할 수 있습니다.

 

"아래는 CircuitBreaker 의 3가지 상태에 대해 이해를 돕기 위한 순서도와 그림입니다."

서킷브레이커 상태 변경

이미지 출처 : https://martinfowler.com/bliki/CircuitBreaker.html

서킷브레이커의 상태는 아래와 같이 변경됩니다.

1. 정상 요청 수행(Closed)

2. 실패 임계치 도달(Closed → Open)

3. 일정시간 소요(Open → Half Open)

4. 요청 수행

     a. 수행 결과 정상 (Half Open → Closed)

     b. 수행 결과 실패 (Half Open → Open) 

 

서킷브레이커 라이브러리 종류

1) Netflix Hystrix

넷플릭스에서 개발한 라이브러리로 MSA 환경에서 분산된 서비스간 통신이 원할하지 않을 경우 각 서비스가 장애 내성과 지연 내성을 갖게하도록 하는 라이브러리
현재는 deprecated 된 상태로 Resilience4j 사용을 권장

 

2) Resilience4j

Netflix Hystrix 로부터 영감을 받아 개발된 Fault Tolerance Library 
Java 전용으로 개발된 경량화된 라이브러리

 

"Netflix Hystrix 공식 doc에서도 Resilience4j 사용을 권장하고 있으니, Hystrix 를 사용할 이유가 없습니다.

Hystrix 에 대해 알아볼 필요 없이 바로 Resilence4j 에 대해 알아보겠습니다."

 

Resilience4j 의 코어 모듈

1) CircuitBreaker : 장애 전파 방지 기능 제공

2) Retry : 요청 실패시 재시도 처리 기능 제공

3) RateLimiter : 제한치를 넘어서 요청을 거부하거나 Queue 생성하여 처리하는 기능 제공

4) TimeLimiter : 실행 시간 제한 설정 기능 제공

5) Bulkhead : 동시 실행 횟수 제한 기능 제공

6) Cache : 결과 캐싱 기능 제공

 

Resilience4j 의 코어 모듈은 위와 같으며 필요한 모듈의 의존성을 설정해 주어 필요한 모듈만 사용할 수 있습니다.

 

Gradle 예시

 
dependencies {
  implementation("io.github.resilience4j:resilience4j-circuitbreaker:${resilience4jVersion}")
  implementation("io.github.resilience4j:resilience4j-ratelimiter:${resilience4jVersion}")
  implementation("io.github.resilience4j:resilience4j-retry:${resilience4jVersion}")
  implementation("io.github.resilience4j:resilience4j-bulkhead:${resilience4jVersion}")
  implementation("io.github.resilience4j:resilience4j-cache:${resilience4jVersion}")
  implementation("io.github.resilience4j:resilience4j-timelimiter:${resilience4jVersion}")
}

Resilience4j 모듈의 우선순위

Retry ( CircuitBreaker ( RateLimiter ( TimeLimiter ( BulkHead ( TargetFunction ) ) ) ) )

위와 같은 우선순위로 모듈이 적용됩니다. (Retry 모듈이 가장 마지막에 적용됨)

 

이를 알아보기 위해 resilience4j jar의 CircuitBreakerConfigurationProperties, RetryConfigurationProperties 클래스 내부를 살펴보면, 

CircuitBreaker 와 Retry 의 Order 값이 각각 -3, -4 로

별도 처리가 없을 경우 CircuitBreaker 가 Retry 보다 우선으로 적용됨을 알 수 있습니다.

 

CircuitBreakerConfigurationProperties

 
public class CircuitBreakerConfigurationProperties extends
    io.github.resilience4j.common.circuitbreaker.configuration.CircuitBreakerConfigurationProperties {

    private int circuitBreakerAspectOrder = Ordered.LOWEST_PRECEDENCE - 3;
    ...
}

RetryConfigurationProperties

 
public class RetryConfigurationProperties extends
    io.github.resilience4j.common.retry.configuration.RetryConfigurationProperties {

    private int retryAspectOrder = Ordered.LOWEST_PRECEDENCE - 4;
    ...
}

CircuitBreakerAspect 

 
@Aspect
public class CircuitBreakerAspect implements Ordered {
   ...
   @Override
    public int getOrder() {
        return circuitBreakerProperties.getCircuitBreakerAspectOrder();
    }
}

AOP 기반하에 동작하므로 우선순위를 바꿔서 적용하고자 할 경우 annotation 방식을 사용하여 layer 를 분리하거나 aspectOrder 속성값을 수정하여 적용할 수 있습니다.

 

Resilience4j Configuration

Resilience4j 의 Configuration 은 yml 파일을 사용하거나, java 코드를 통해 설정할 수 있습니다.

1) yml 파일을 사용한 Config 예시

 
resilience4j.circuitbreaker:
    configs:
        default:
            slidingWindowSize: 100
            waitDurationInOpenState: 10000
            permittedNumberOfCallsInHalfOpenState: 30
            failureRateThreshold: 60
            eventConsumerBufferSize: 10
        custom:
            slidingWindowSize: 50
            permittedNumberOfCallsInHalfOpenState: 10
            ... 생략

2) Java 코드를 통한 Config 예시

 
@Configuration
class CircuitBreakerProvider(
    val circuitBreakerRegistry: CircuitBreakerRegistry,
) {

    companion object {
        const val CIRCUIT_MEMDB: String = "CB_MEMDB"
    }

    @Bean
    fun memDBCircuitBreaker(): CircuitBreaker {
        return circuitBreakerRegistry.circuitBreaker(            
            CIRCUIT_MEMDB, CircuitBreakerConfig.custom()
                .failureRateThreshold(10F)  // 실패비율 10% 이상시 서킷 오픈
                .slowCallDurationThreshold(Duration.ofMillis(500))  // 500ms 이상 소요시 실패로 간주
                .slowCallRateThreshold(10F) // slowCallDurationThreshold 초과 비율이 10% 이상시 서킷 오픈
                .waitDurationInOpenState(Duration.ofMillis(60000))   // OPEN -> HALF-OPEN 전환 전 기다리는 시간
                .minimumNumberOfCalls(5) // 집계에 필요한 최소 호출 수
                .slidingWindowSize(5)    // 서킷 CLOSE 상태에서 5회 호출 도달시 failureRateThreshold 실패비율 계산
                .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED)    // 호출 횟수 기준 계산 (TIME_BASED는 시간 기준)
                .ignoreExceptions(StockManageException::class.java)   // 화이트리스트로 서킷 오픈 기준 ex 관리
                .build()
        )
    }

 

"Resilience4j 모듈 중 가장 많이 사용되는 CircuitBreaker, Retry 모듈의 속성값에 대해 간단히 알아보겠습니다."

Resilience4j CircuitBreaker Property

property설명
failureRateThreshold 실패비율 임계치를 백분율로 설정 해당 값을 넘어갈 시 Circuit Breaker 는 Open상태로 전환되며, 이때부터 호출을 차단한다 (기본값: 50)
slowCallRateThreshold 임계값을 백분율로 설정, CircuitBreaker는 호출에 걸리는 시간이 slowCallDurationThreshold보다 길면 느린 호출로 간주, 해당 값을 넘어갈 시 Circuit Breaker 는 Open상태로 전환되며, 이때부터 호출을 차단한다 (기본값: 100)
slowCallDurationThreshold 호출에 소요되는 시간이 설정한 임계치보다 길면 느린 호출로 계산한다. -> 응답시간이 느린것으로 판단할 기준 시간 (60초, 1000 ms = 1 sec) (기본값: 60000[ms])
permittedNumberOfCallsInHalfOpenState HALF_OPEN 상태일 때, OPEN/CLOSE 여부를 판단하기 위해 허용할 호출 횟수를 설정 수 (기본값: 10)
maxWaitDurationInHalfOpenState HALF_OPEN 상태로 있을 수 있는 최대 시간이다. 0일 때 허용 횟수 만큼 호출을 모두 완료할 때까지 HALF_OEPN 상태로 무한정 기다린다. (기본값: 0)
slidingWindowType sliding window 타입을 결정한다. COUNT_BASED인 경우 slidingWindowSize만큼의 마지막 call들이 기록되고 집계됩니다.
TIME_BASED인 경우 마지막 slidingWindowSize초 동안의 call들이 기록되고 집계됩니다. (기본값: COUNT_BASED)
slidingWindowSize CLOSED 상태에서 집계되는 슬라이딩 윈도우 크기를 설정한다. (기본값: 100)
minimumNumberOfCalls minimumNumberOfCalls 이상의 요청이 있을 때부터 faiure/slowCall rate를 계산한다. 예를들어, 해당값이 10이라면 최소한 호출을 10번을 기록해야 실패 비율을 계산할 수 있다.
기록한 호출 횟수가 9번뿐이라면 9번 모두 실패했더라도 circuitbreaker는 열리지 않는다. (기본값: 100)
waitDurationInOpenState OPEN에서 HALF_OPEN 상태로 전환하기 전 기다리는 시간 (60초, 1000 ms = 1 sec) (기본값: 60000[ms])
recordExceptions 실패로 기록할 Exception 리스트 (기본값: empty)
ignoreExceptions 실패나 성공으로 기록하지 않을 Exception 리스트 (기본값: empty)
ignoreException 기록하지 않을 Exception을 판단하는 Predicate<Throwable>을 설정 (커스터마이징, 기본값: throwable -> true)
recordFailure 어떠한 경우에 Failure Count를 증가시킬지 Predicate를 정의해 CircuitBreaker에 대한 Exception Handler를 재정의하는 것이다. true를 return할 경우, failure count를 증가시키게 된다 (기본값: false)

 

Resilience4j Retry Property

property설명
maxRetryAttempts 최대 재시도 수(최초 호출도 포함, 기본값 3)
waitDuration 재시도 할 때마다 기다리는 고정시간 (1초[1000ms], 기본값: 0.5초[500ms])
retryOnResultPredicate 반환되는 결과에 따라서 retry를 할지 말지 결정하는 filter, true로 반환하면 retry하고 false로 반환하면 retry 하지 않습니다. (기본값: (numOfAttempts,Either<throwable, result) -> waitDuration)
retryExceptionPredicate 예외(Exception)에 따라 재시도 여부를를 결정하기 위한 filter, 만약 예외에 따라 재시도해야 한다면 true를, 그 외엔 false를 리턴해야 한다. (기본값: result -> false)
retryExceptions 실패로 기록되는 블랙리스트 예외. empty일 경우 모든 에러 클래스를 재시도 한다. (기본값: empty)
ignoreExceptions 무시되어야 하는 예외(화이트리스트) 즉, 재시도 되지 않아야 할 에러 클래스 리스트이다. (기본값: empty)
failAfterMaxRetries 설정한 maxAttempts 만틈 재시도하고 나서도 결과가 여전히 retryOnResultPredicate를 통과하지 못했을 때 MaxRetriesExceededException 발생을 활성화/비활성화하는 boolean (기본값: false)

 

* 그외 모듈에 대한 속성값이 궁금하시다면 아래의 Resilience4j 공식 document 를 참고해주세요.

 

 

 

참고 : 

https://resilience4j.readme.io/docs/getting-started

 

반응형

[ 인수테스트 (AT : Acceptance Test) ]

인수테스트란 요구사항을 작성하는데 집중.

블랙박스 테스트의 성격을 가지므로 시스템 내부 코드를 직접 호출하는 것이 아닌 외부에서 요청하는 방식으로 검증.

* 블랙박스란? 결과물의 내부 구현이나 사용된 기술을 기반으로 검증하기 보단 표면적으로 확인할 수 있는 요소를 바탕으로 검증

* ATDD(Acceptance Test-Driven Development)는 기획 단계부터 인수 테스트를 통해 공통의 이해를 도모해 프로젝트를 진행하는 방법

 

@SpringBootTest 웹서버를 사용하여 테스트

webEnvironment 설정을 통해 웹 서버 환경을 지정

 

[ MockMvc vs RestAssured vs WebTestClient ]

1. MockMvc

@SpringBootTest 의 webEnvironment.MOCK과 함께 사용가능.

mocking된 web environment(ex Tomcat) 환경에서 테스트

2. WebTestClient

@SpringBootTest의 webEnvironment.RANDOM_PORT나 DEFINED_PORT와 함께 사용, Netty를 기본으로 사용

3. RestAssured

실제 web environment(Apache Tomcat)을 사용하여 테스트

 

[ RestAssured 사용법 ]

RestAssured.given().log().all()

.body(params)

.contentType(MediaType.APPLICATION_JSON_VALUE)

.when()

.post("/~")

.then().log().all()

.statusCode(HttpStatus.CREATED.value())

.header("", notNullValue();

 

[ 인수 조건 작성법 ]

Feature: 간략한 기능 서술
  Scenario: 시나리오 제목
    Given: 사전조건
    And: 앞선 내용의 추가적인 내용
    And: 앞선 내용의 추가적인 내용
    When: 발생해야하는 이벤트
    Then: 사후조건
반응형

영속성 

CascadeType.ALL: 모든 Cascade를 적용
CascadeType.PERSIST: 엔티티를 영속화할 때, 연관된 엔티티도 함께 유지
CascadeType.MERGE: 엔티티 상태를 병합(Merge)할 때, 연관된 엔티티도 모두 병합
CascadeType.REMOVE: 엔티티를 제거할 때, 연관된 엔티티도 모두 제거
CascadeType.DETACH: 부모 엔티티를 detach() 수행하면, 연관 엔티티도 detach()상태가 되어 변경 사항 반영 X
CascadeType.REFRESH: 상위 엔티티를 새로고침(Refresh)할 때, 연관된 엔티티도 모두 새로고침

 

Question N : User 1 관계일 때

User user = new User(?);

Question question = Question.builder(?).title(?).user(user).build();

questionRepository.save(question);

할 경우 exception 발생.

=> Question Entity 연관관계 매핑(@ManyToOne)에서 CascadeType.PERSIST 를 지정할 경우 User 객체도 영속성이 전이됨(영속화)

 

 

https://zzang9ha.tistory.com/350

 

반응형

'back > JPA' 카테고리의 다른 글

[JPA] equals , hashcode  (0) 2022.11.08
[JPA] JPQL, QueryDSL  (0) 2020.03.21
[JPA] JPA 영속성 컨텍스트  (0) 2020.03.08
[JPA] 연관관계 매핑 : 단방향, 양방향 매핑  (0) 2020.03.01
[JPA] JPA 기초 설정 및 entity 필드 매핑  (0) 2020.02.27

[ Object equals ]

equals 메서드는 같은 객체(인스턴스) 일 경우에만 동일하다고 판단.

reflexive : x.equals(x) 는 항상 참

symmetric : x.equals(y) 가 참이라면 y.equals(x) 역시 참이어야 한다

transitive : x.equals(y) 가 참이고 y.equals(z) 가 참일 때 x.equals(z) 역시 참이어야 한다

consistent : x.equals(y)가 참일 때 equals 메서드에 사용된 값이 변하지 않는 이상 몇번을 호출해도 같은 결과가 나와야 한다

x가 null이 아닐 때 x.equals(null) 은 항상 거짓이어야 한다

 

[ JPA 에서의 equals ]

엔티티 매니저의 영속성 컨텍스트에서 1차 캐시를 이용해 같은 ID의 엔티티를 항상 같은 객체로 가져올 수 있다. 하지만 1차 캐시를 초기화 한 후 다시 데이터베이스에서 동일한 엔티티를 읽어오는 경우 초기화 전에 얻었던 객체와 초기화 이후에 얻은 객체가 서로 다른 객체로 생성된다.

이는 equals 메서드의 consistent 원칙을 위반하는 것이며 엔티티는 자바 객체라기 보단 데이터베이스 테이블 레코드에 가깝기 때문에 엔티티 객체의 필드(pk)가 동일하다면 같은 레코드, 즉 객체라고 판단해야 한다. 

이와 같은 이유로 equals 메서드와 hashCode 메서드를 재정의 해야 한다.

 

* 준영속 상태의 엔티티 간 비교, 비교할 두 인자가 둘 다 null 인 상태에서의 비교 등을 고려하여 

Objects.hash 메서드를 사용하여 hashCode 구현

 

https://velog.io/@park2348190/JPA-Entity%EC%9D%98-equals%EC%99%80-hashCode

 

반응형

'back > JPA' 카테고리의 다른 글

[JPA] cascade  (0) 2022.11.10
[JPA] JPQL, QueryDSL  (0) 2020.03.21
[JPA] JPA 영속성 컨텍스트  (0) 2020.03.08
[JPA] 연관관계 매핑 : 단방향, 양방향 매핑  (0) 2020.03.01
[JPA] JPA 기초 설정 및 entity 필드 매핑  (0) 2020.02.27

[ 정적팩토리메소드 패턴 ]

https://7942yongdae.tistory.com/147

 

[ 정적팩토리메소드 패턴 네이밍 룰 ]

1. from: 하나의 매개변수를 받아서 해당 타입의 인스턴스 생성
2. of: 여러개의 매개변수를 받아서 인스턴스를 생성
3. instance or getInstance: 인스턴스를 반환하지만 동일한 인스턴스임을 보장하지 않는다.
4. create or newInstance: instance 혹은 getInstance와 같지만, 매번 새로운 인스턴스를 생성하여 반환함을 보장.
5. getType: getInstance와 같으나 생성할 클래스가 아닌 다른 클래스에 팩토리 메소드를 정의할 때 사용. (호출하는 클래스와 다른 타입의 인스턴스를 반환할때 사용)

FileStore fs = Files.getFileStore(path);

6. newType: getType과 같지만 매번 새로운 인스턴스를 반환

7. type : getType 과 newType의 간결한 버전

List<Test> list = Collections.list(test);

https://velog.io/@saint6839/%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C-%EB%84%A4%EC%9D%B4%EB%B0%8D-%EB%B0%A9%EC%8B%9D

 

[ 생성자에 코드를 넣지 말자 ]

https://cozzin.tistory.com/71

 

※ 디미터법칙

https://tecoble.techcourse.co.kr/post/2020-06-02-law-of-demeter/

1. 객체 자신의 메서드들

2. 메서드의 파라미터로 넘어온 객체들의 메서드들

3. 메서드 내부에서 생성, 초기화된 객체의 메서드들

4. 인스턴스 변수로 가지고 있는 객체가 소유한 메서드들

 

 

반응형

[ 일급콜렉션 : First Class Collection ]

Collection 을 Wrapping 한 클래스

규칙 8: 일급 콜렉션 사용
이 규칙의 적용은 간단하다.
콜렉션을 포함한 클래스는 반드시 다른 멤버 변수가 없어야 한다.
각 콜렉션은 그 자체로 포장돼 있으므로 이제 콜렉션과 관련된 동작은 근거지가 마련된셈이다.
필터가 이 새 클래스의 일부가 됨을 알 수 있다.
필터는 또한 스스로 함수 객체가 될 수 있다.
또한 새 클래스는 두 그룹을 같이 묶는다든가 그룹의 각 원소에 규칙을 적용하는 등의 동작을 처리할 수 있다.
이는 인스턴스 변수에 대한 규칙의 확실한 확장이지만 그 자체를 위해서도 중요하다.
콜렉션은 실로 매우 유용한 원시 타입이다.
많은 동작이 있지만 후임 프로그래머나 유지보수 담당자에 의미적 의도나 단초는 거의 없다. - 소트웍스 앤솔로지 객체지향 생활체조편

[ 일급콜렉션의 예 ]

// 콜렉션 사용
final List<Integer> lottoTicket = new ArrayList<>();
lottoTicket.add(4);
lottoTicket.add(11);
lottoTicket.add(15);
lottoTicket.add(21);
lottoTicket.add(33);
lottoTicket.add(45);
// 일급콜렉션 (Wrapping)
public class LottoTicket {
	
    private final List<Integer> lottoNumbers;
    
    public LottoTicket(List<Integer> lottoNumbers){
    	this.lottoNumbers = lottoNumbers;
    }
}

 

[ 일급콜렉션을 왜 써야 하는가? ]

1. 비지니스에 종속적인 자료구조 : Wrapper 클래스(일급콜렉션 클래스) 내에서 콜렉션에 대한 유효성 검사 및 정제 등의 로직을 포함하여 비지니스에 필요한 자료구조를 만들 수 있다. (ex: LottoTicket 내에서 Lotto 번호 범위(1~45)에 대한 유효성 검사 로직을 추가)

2. 불변 : 콜렉션 변수를 final 로 선언시 재할당만 불가할 뿐, 콜렉션에 .set 등이 가능하여 불변성 보장이 되지 않는 반면 일급콜렉션 사용시 선언 후엔 조작이 불가.

3. 상태와 행위를 한 곳에서 관리 : 값과 로직이 함께 존재 (ex: 일급콜렉션 클래스 내에 콜렉션의 합계를 구하는 메소드를 구현하여 상태와 행위를 한 곳에서 관리)

4. 이름이 있는 컬렉션 : Wrapping 한 클래스를 사용하므로 인스턴스 생성시 new ArrayList<>() 대신 new LottoTicket(createNonDuplicatedNumbers()) 이 가능.

 

 

* 일급콜렉션에서 Wrapping 한 인스턴스 변수를 외부에서 필요로 할 땐?

Collections.unmidifiableList 를 사용하여 리턴하여 외부에서의 변경을 막는다.

 

출처 및 참고 : 

https://jojoldu.tistory.com/412

 

https://velog.io/@injoon2019/%EC%9D%BC%EA%B8%89%EC%BB%AC%EB%A0%89%EC%85%98%EC%9D%98-%EB%B6%88%EB%B3%80%EA%B0%9D%EC%B2%B4-unmodifiable-%EB%B0%A9%EC%96%B4%EC%A0%81-%EB%B3%B5%EC%82%AC

반응형

'back' 카테고리의 다른 글

[TDD] 테스트주도개발  (0) 2022.10.04
JWT  (0) 2020.11.18
[gradle] jar build, war build  (0) 2020.03.25

[ TDD란 ]

Test Driven Development 의 약자로 테스트 코드를 작성하고 프로덕션 코드를 개발하는, 테스트에서 부터 개발이 이뤄지는 테스트가 주도하는 개발 방법.  

TDD = TFD(Test First Development) + 리팩토링

 

[ TDD 원칙 ]

실패하는 단위 테스트를 작성할 때 까지 프로덕션 코드를 작성하지 않는다.

컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성.

현재 실패하는 테스트를 통과할 정도로만 실제 코드 작성.

 

[ TDD 는 어떻게 해야하나 ]

요구사항 분석을 통한 대략적인 설계를 한 후 객체를 추출

UI, DB 등과 의존관계를 가지지 않는 핵심 도메인 영역을 집중 설계

Controller , View 보단 우선적으로 Domain(Model) 영역을 1차적으로 단위테스트 작성

 

 

 

반응형

'back' 카테고리의 다른 글

[Java] 일급콜렉션 : First Class Collection  (0) 2022.10.06
JWT  (0) 2020.11.18
[gradle] jar build, war build  (0) 2020.03.25

[ GC : Garbage collector ]

JVM 메모리는 크게 class / stack / heap 영역으로 나누어지며,

GC 는 heap 영역을 대상으로 수행된다.

 

Heap 영역은 

Young / Old / Perm 영역으로 나눌 수 있으며 (java 8부터 Perm 영역은 제거되었음)

Young 영역은 

Eden , survivor 0, survivor 1 로 나눌 수 있다.

 

최초 객체 생성시 Eden 영역에 할당되며,

Eden 영역이 다 찼을 경우 reachable (접근 가능한 상태) 한 객체는 survivor 0로 옮기고 Eden 영역을 정리한다 (minor GC)

반복적으로 Eden 영역을 정리하다 survivor 0 이 다 찬 경우, reachable 한 객체를 survivor 1 로 옮긴 후 survivor 0 을 비워준다.

객체의 age 가 특정 임계점 도달시 Old 영역으로 객체를 옮겨준다 (promote)

위 과정을 반복하다 Old 영역도 다 차게되면 Full GC (major GC) 가 발생한다.

 

GC 수행시 아래 작업을 공통적으로 수행

Mark and Sweep : Mark(사용되는 메모리와 사용되지 않는 메모리 구분), Sweep(사용되지 않는 메모리 해제)

Stop the world : GC 수행하는 쓰레드를 제외한 모든 쓰레드 작업 중단

 

[ GC 알고리즘 종류 ]

1. Serial GC : 싱글쓰레드로 GC 수행

2. Parallel GC : Young 영역 GC 를 멀티쓰레드로 수행 (java 8)

3. Parallel Old GC : Young , Old 영역 모두 GC를 멀티쓰레드로 수행 (java 8)

3. CMS GC : 

4. G1 GC : Heap 을 일정한 크기의 region 으로 나눔. (java 9)

 

 

 

https://mangkyu.tistory.com/118

https://velog.io/@injoon2019/CS-%EC%A0%95%EB%A6%AC-GC-Garbage-Collector

 

 

 

 

 

반응형

+ Recent posts