Hyunsuk's Blog

Hyunsuk's Blog

목표

  • 전략 패턴 사용
  • 옵저버 패턴 사용

1.전략 패턴 (Strategy Pattern)

사용 시점

  • 유사한 기능을 하는 여러 클래스가 존재할 때
    • 예: 다양한 결제 수단 (신용카드, 카카오페이, 토스페이)
    • 예: 다양한 인증 방식 (JWT, OAuth, Basic Auth)
    • 예: 다양한 캐싱 전략 (Redis, Memcached, Local Cache)
    • 예: 다양한 로깅 방식 (File, Console, Cloud)

구현 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from abc import ABC, abstractmethod

# 결제 수단 인터페이스 (Strategy)
class PaymentStrategy(ABC):
@abstractmethod
def pay(self, amount):
pass

# 결제 수단 구현 (Concrete Strategies)
class CreditCardPayment(PaymentStrategy):
def pay(self, amount):
print(f"신용카드로 {amount}원 결제 완료!")

class KakaoPayPayment(PaymentStrategy):
def pay(self, amount):
print(f"카카오페이로 {amount}원 결제 완료!")

# 결제 프로세서 (Context)
class PaymentProcessor:
def __init__(self, payment_strategy: PaymentStrategy):
self.payment_strategy = payment_strategy

def pay(self, amount):
self.payment_strategy.pay(amount)

# 사용 예시
payment = PaymentProcessor(KakaoPayPayment())
payment.pay(10000)

2.옵저버 패턴 (Observer Pattern)

사용 시점

  • 한 객체의 상태 변화를 다른 객체들에게 알려야 할 때
    • 예: 결제 상태 변경 알림
    • 예: 이벤트 처리 시스템 (버튼 클릭, 키보드 입력)
    • 예: 실시간 데이터 업데이트 (주식 가격, 날씨 정보)
    • 예: 사용자 인터페이스 업데이트 (데이터 변경 시 화면 갱신)

구현 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 옵저버(Observer) 인터페이스
class Notifier:
def send_notification(self, message):
pass

# 알림 방식 구현
class SMSNotifier(Notifier):
def send_notification(self, message):
print(f"📱 SMS: {message}")

class EmailNotifier(Notifier):
def send_notification(self, message):
print(f"📧 Email: {message}")

# 결제 프로세서 (Subject)
class PaymentProcessor:
def __init__(self):
self.notifiers = []

def add_notifier(self, notifier: Notifier):
self.notifiers.append(notifier)

def pay(self, amount):
print(f"💳 {amount}원 결제 완료!")
for notifier in self.notifiers:
notifier.send_notification(f"{amount}원 결제가 완료되었습니다.")

# 사용 예시
payment = PaymentProcessor()
payment.add_notifier(SMSNotifier())
payment.add_notifier(EmailNotifier())
payment.pay(20000)

정리

각 디자인 패턴은 특정 상황에서 코드의 유지보수성과 확장성을 높이는 데 도움을 줍니다. 실제 프로젝트에서는 이러한 패턴들을 적절히 조합하여 사용하는 것이 중요합니다.

목표

docker 카프카 RKaft 모드(no zookeepler) server 만들기

과정

  1. 윈도우 wsl 우분투 설치
  2. 카프카 docker pull 하기
  3. 카프카 실행시키기
  4. 메세지 보내보기

윈도우에 리눅스 설치

  1. 윈도우 기능 켜기 끄기에서 linux용 하위 시스템 활성화
  1. wsl -l -o 입력하여 설치 가능한 배포 확인
  2. wsl –install [배포판]으로 설치
  3. wsl 입력해서 가상 리눅스로 접속
더 읽어보기 »

목표

junit 기본 기능 테스트해보기


주 사용 어노테이션

@Test: 테스트 메서드를 나타냅니다. 이 어노테이션이 붙은 메서드는 테스트 프레임워크에 의해 실행
@BeforeEach: 각 테스트 메서드가 실행되기 전에 실행. 테스트 초기화 작업에 사용
@AfterEach: 각 테스트 메서드가 실행된 후에 실행. 테스트 정리 작업에 사용
@BeforeAll: 모든 테스트 메서드가 실행되기 전에 한 번 실행. 주로 클래스 레벨의 초기화 작업에 사용
@AfterAll: 모든 테스트 메서드가 실행된 후에 한 번 실행. 주로 클래스 레벨의 정리 작업에 사용
@Disabled: 테스트 메서드나 클래스가 실행되지 않음. 주석 처리와 비슷한 역할
@DisplayName: 테스트 메서드의 이름을 지정. 테스트 결과 보고서에 표시


주 사용 펑션들

assertEquals(expected, actual): 두 값이 같은지 확인
assertNotEquals(unexpected, actual): 두 값이 같지 않은지 확인
assertTrue(condition): 조건이 참인지 확인
assertFalse(condition): 조건이 거짓인지 확인
assertNull(object): 객체가 null인지 확인
assertNotNull(object): 객체가 null이 아닌지 확인
assertThrows(expectedType, executable): 예외가 발생하는지 확인

더 읽어보기 »

목표

일반 thread 와 가상 thread 속도 비교하기(IO작업)

일반 thread 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class PlatformThreadTest {
public static void main(String[] args) throws Exception {
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.uri(new URI("http://localhost:4000/"))
.build();

int totalRequests = 100000;
int batchSize = 100;
int numBatches = totalRequests / batchSize;

Instant start = Instant.now();

for (int batch = 0; batch < numBatches; batch++) {
var executor = Executors.newFixedThreadPool(batchSize);
CompletableFuture<?>[] futures = new CompletableFuture<?>[batchSize];

for (int i = 0; i < batchSize; i++) {
futures[i] = CompletableFuture.runAsync(() -> {
try {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// System.out.println("Response code: " + response.statusCode());
} catch (Exception e) {
e.printStackTrace();
}
}, executor);
}

CompletableFuture.allOf(futures).join();
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
}

Instant end = Instant.now();
System.out.println("Time taken with platform threads: " + Duration.between(start, end).toMillis() + " ms");
}
}
더 읽어보기 »

sqlalchemy pool_recycle옵션

  • 세팅한 값(초) 후에 db 커넥션 풀을 다시 만든다. 디폴트는 -1

언제 세팅해야할까?

  • mysql 처럼 디폴트로 커넥션 타임 아웃이 있는 db를 사용할 때 설정해줘야 한다 mysql을 사용하는데 pool_recycle 세팅을 안 한하고 배치 같은 작업을 돌린다면
    1
    sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (2006, 'MySQL server has gone away')
    에러를 보게 될 것이다

사용 예시

1
2
POOL_RECYCLE_TIME = 60 * 60 * 4
engine = create_engine(f"mariadb+pymysql://{user}:{passwd}@{host}:{port}/{database}?charset=utf8mb4", pool_recycle=POOL_RECYCLE_TIME)
더 읽어보기 »
0%