728x90

배경

최근에 회사에서 알림톡 프로세스를 개선했다. 자세한 내용: 여기

 

알림톡 기능 퍼포먼스 개선

배경기존에 A 서버에서 B 서버로 호출 하면 써드파티 알림톡 발송업체로 호출 하고 다시 A 서버로 콜백 하는 방식이었다. (A 서버 에서는 해당 발송 결과 내역을 저장한다.) N번의 알림톡 발송 요

ydontustudy.tistory.com

 

 

알림톡 발송 후 콜백을 받아 람다에서 후처리 하는 프로세스까지는 성공적이었다. 람다에 대해 더 알아보며 Reserved Concurrency/Provisioning Concurrency 가 있었다. 여기서 파생해서 람다에 대해 더 알게 된 이후로 현재 설계로는 서비스가 장기적으로 잘 돌아가지 않을 것이고 큰 기술부채로 남겨질 것 같다고 생각해서 더 고민을 해보았다.

 

해결 방법 모색

 

Reserved Concurrency 에 대한 고민

 

람다는 동시에 1000개의 함수가 실행될 수 있는데, 해당 Region의 모든 람다가 통틀어 1000개 라고 한다. 즉, 여러개의 함수가 있을 때, 중요하지 않은 함수의 요청이 갑자기 많아지만 정작 중요한 함수에 대한 요청이 거부되면서, 실행하지 못하는 이슈가 발생할 수 있다. 그래서 전체 1000개의 pool에서 특정 중요한 함수에 할당 해놓기 위해서 Reserved Concurrency 를 설정 해둘 필요성을 느꼈다.

(추가적으로, 해당 설정으로 인해 디도스나 비용절감에도 도움이 될 것 같다.)

 

그래서 알림톡 후처리를 하는 람다 함수에 Reserved Concurrency를 300개 정도 설정 해놓고, 해당 함수에 대한 전체 요청은 어플리케이션내에서 처리하는 방향이 낫지 않을까 생각 해보았다. 해당 기능은 LB 환경에 대한 고려도 하지 않아도 된다. 왜냐하면 람다로 보내질 이벤트에 담길 정보들은 Notify가 아닌 다른 서버에서 요청을 받기 때문에 데이터 정합성이 이미 확보 돼있고, 순서에 크게 상관없기 때문이다.

 

그리고 알림톡 프로세스 개선을 V4 까지 잡아 놓았는데, V2 부터는 람다에서 메시지 큐에 들어갈 이벤트를 발행 할 예정이다. 이러한 부분에서도 내부 큐에서 번들로 람다에 요청 보내는것이 필요하다고 생각한다. 메시지 큐 이벤트 하나하나 발행할 때마다 람다에 요청하기엔

람다의 컨넥션 풀이 한정적이고 Reserved Concurrency 에 설정 된 임계값이 넘어가면 throttling이 일어나기 때문에 내부적으로 번들링 하는거는 현재 상황에서 필수불가결하지 않을까 생각해본다.

 

 

Provisioned Concurrency 에 대한 고민

 

람다를 사용하는 많은 분들이 고민하는 부분중 하나는 Cold start 일것이다. 요청을 받고 인스턴스가 띄워지는 시간 동안은 요청을 처리하지 못하기 때문에 그만큼 고객은 기다리는 시간도 정비례한다. 이거를 해결하기 위해서는 Provisioned Concurency 를 설정하면 된다. 해당 기능은 미리 Warm up 될 람다 인스턴스의 개수를 설정 하면, 바로 처리 할 수 있게 그만큼 함수를 warm up 시켜놓는다. 그러나 해당 기능은 비용이 발생한다.


개선 하려고 하는 프로세스는 실시간성을 띄고있지 않기 때문에 비용을 부담하면서 까지 해당 기능이 필요하지 않을 것 같다.

 

 

마무리

기술 하나를 도입할 때 양보다 질이라고 생각이 든다. 좋다고 하는 기술들을 이것저것 덕지덕지 붙이다 보면 미래에는 큰 부채가 될 가능성이 많을 것 같다. 그래서 최대한 많이 알아보고 테스트도 거쳐 보아야 할 것 같다.

728x90
findDateBetween(LocalDateTime arg1, LocalDateTime arg2)

 

 

이렇게 조회 했더니 안됐다. 이유를 찾아 보니

 

byte[], byte[] 로 인식한다고 한다.

 

해결 방법은

 

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-java8</artifactId>
    <version>${hibernate.version}</version>
</dependency>

 

 

이걸 추가 하면 된다고 한다.

 

그래서 추가 했더니 잘 됐다.

 

그러나 굳이 추가 해서 프로젝트 용량을 더 늘릴 필요성을 못느껴서 LocalDateTime을 Date 타입으로 컨버팅 하는 정적 유틸 메소드가 있어서 해당 메소드를 사용했다.

 

 

 

참조

https://developer-syubrofo.tistory.com/289 [공부는 관성이다.:티스토리]

 

728x90

배경

기존에 A 서버에서 B 서버로 호출 하면 써드파티 알림톡 발송업체로 호출 하고 다시 A 서버로 콜백 하는 방식이었다. (A 서버 에서는 해당 발송 결과 내역을 저장한다.) N번의 알림톡 발송 요청이 생기면 A서버가 즉시 N번의 콜백 요청을 받아 수행했었다. 이 부분에서 회사 트래픽이 올라가면서 cpu 사용량이 많이 올라가기 시작했다.

그래서 회사 동료분중 한 분이 해당 부분을 개선하기 위해 A 서버에서 콜백을 받고 바로 처리 하는게 아니라, 콜백을 받으면 카프카에 이벤트를 저장하는 방식으로 바꾸셨다. 그런데 여기서도 cpu 부하가 걸렸다. (A 서버에서 카프카로 이벤트를 발행만 하는데도 부하가 생겼다.) 그래서 내가 해당 이슈에 대해서 곰곰히 생각해 보았다. 처음에 해당 로직의 성격상 A 서버에서 처리를 하는게 맞는가를 따져 보았을 때, 그건 맞았다. 그래서 해당 레거시 코드를 작성하신 분을 어느정도 이해할 수 있었다. 
그러나 이미 A 서버는 무거워서 간당간당한 상황이었다. 이벤트를 발행 하는 역할은 AWS Lambda를 사용 해봐도 괜찮겠다는 생각이 들어 말씀 드렸었다. 이후 자연스럽게 내가 프로젝트에 참여하게 되었다. 

 

해결 방법 모색

처음에 이벤트 발행을 Lambda 에서 하는 것을 시작으로 생각을 이어 나갔다. 그 와중에 Produce/Consume 둘 다 람다에서 해도 되겠다는 생각이 들었다. 공유를 드리고 개발을 해보았다. 지금까지 나는 이 회사에 들어와서 카프카를 접해볼 시간이 없었다. 이번에 처음 접해 보니 회사 넥서스에 회사 전용 카프카 전용 객체가 있었고 기존에 카프카는 이와 함께 맞물려 돌아 가고 있었다. 이를 처음으로 node.js 환경에서 돌아갈 수 있게 새로 개발 해야 하는 공수를 들여야 했다. 하면 되겠지만 이미 할 일도 많고 굳이 이렇게 까지 할 필요가 있을까 라는 의견들이 있었다. 그리고 무엇 보다 더 크리티컬한 이유는 기존에 카프카를 사용할 때 실패한 이벤트에 대한 전략이 세워져 있지않아서 이 상태로 람다에다가 적용 할 npm 라이브러리를 만들면 기술 부채를 그대로 옮겨 놓는 것 이라고 판단 했다.
결론은 현재 트래픽으로는 굳이 카프카를 사용 할만한 처리량을 갖고 있지 않은 상태에서 적용 하는건 오버엔지니어링이고 실패한 이벤트에 대한 전략이 없는 상태에서 npm 라이브러리를 만들면 기술부채를 쌓는 격이라고 생각했다. 그래서 회사의 발전 상황에 따라 조금씩 발전 시키기로 결정 하여 버전으로 나누어서 청사진을 그려 보았다. 

 

- V1
    - Lambda 에서 바로 DB에 쌓기

 


- V2
    - Lambda 에서 Produce하기

 


- V3
    - Lambda 에서 Produce/Consume 하기

 


- V4
    - Transactional Outboxing Pattern 적용하기

 

 

마무리

이번 고민을 통해 여러 방면에서 퍼포먼스 개선을 고민할 수 있었다. 메시지 큐를 직접 다뤄본 적은 이번이 처음이었지만, 많은 회사에서 이를 사용하는 이유를 이해하게 되었다. '대용량'이라는 개념이 사람마다 다르고 회사의 서버 스펙에 따라 달라질 수 있어 명확히 정의하기 어려운 단어라는 점도 느꼈다. 카프카가 대용량 트래픽에 적합하다는 평가를 받지만, 현재 회사 상황에서는 오버엔지니어링이 될 수 있다는 고민을 많이 했다. 또한, 이번 기회를 통해 이벤트 관리의 중요성에 대해서도 깊이 생각해보게 되었다. 전체적으로 메시지 큐의 쓰임새에 대해 더 깊이 이해할 수 있는 유익한 시간이었다.

728x90

들어가며

개인 프로젝트에서 유닛 테스트를 짜고 있었다. SMS, 이메일 인증 로직을 테스트 하려고 하다가 고민에 빠졌다. 해당 로직은 외부 캐시 서비스를 사용 해서 인증번호 대조를 진행한다. 이를 테스트 하기 위한 방법론에 대한 고민에 빠진 것이다. 고민은 Spy 객체를 만들어서 코드로 로직을 검증할까, 아니면 실제 데이터를 가지고 검증을 할까 고민이었다. 해당 서비스는 외부 서비스를 의존하는 서비스라서 자바 코드로 검증하기엔 단지 flow만 검증하는 느낌이었다. 그래서 flow 뿐만 아니라 데이터도 검증 해서 더 정확히 검증 할 수 있게 실제로 Redis를 붙여서 검증하기로 결정 하였다. Mockist인 나로서는 고민이 되는 부분이었다.

 

해결

애플리케이션을 전부 올리는 SpringBootTest를 사용하기엔 테스트 시간을 너무 소비하는 느낌이어서 생산성이 떨어질 것 같았다. 그래서 test profile 전용 application yaml 파일을 만들고 테스트에 쓰일 Redis config 파일을 만들었다. 그 다음으로 Redis를 실행 시켜야 하는데 배포 전에 CI 가 실행 되는 github actions VM 에 매번 이러한 테스트가 생길 때마다 해당 하는 이미지를 올리는 등 이러한 관리 공수가 들것이라고 생각했다. 매번 CI script 코드도 수정 해야 하는데, 테스트 코드와 script 싱크를 맞추는 것도 추후 서비스가 더 커지면 귀찮은 일이라고 느껴질 수 있겠다고 생각했다. 그래서 이것도 자동화 하고싶었다. 그래서 github actions VM에 도커 환경을 세팅 해준 후 ./gradlew build 을 해주면 자동으로 @Testcontainer가 붙은 클래스에서 필요한 이미지를 세팅 해주는 테스트 환경을 구성했다.

 

기대할 수 있는 점

Testcontainer를 활용하면 앞으로 다양하게 테스트를 운영 환경이랑 최대한 비슷하게 해볼 수 있을거라고 기대한다. 지금까지 integration test는 H2 in-memory 디비로 했었지만 한계가 있었다. 호환하지 않은 문법 등 많아서 테스트를 위해 관리 해야 할 점들이 많았었다. 그런점에서 운영 환경이랑 비슷하다고 생각이 안되었다. (그렇다고 해서 도커를 띄워서 운영 환경을 만들 수 없다는건 아니긴 하다. 만들 수는 있지만 억지로 만들어주는 느낌으로 운영 코드랑 필요한 이미지를 싱크를 CI script로 맞춰 줘야 하는 단점이 있다.) 그러나 Testcontainer는 필요한 이미지를 코드단에서 관리가 가능하기 때문에 테스트에 더 집중할 수 있다고 생각한다. 그리고 코드로 운영 환경을 최대한 구성할 수 있어서 테스트를 실제 운영환경이랑 비슷하게 할 수 있다는 장점이 있었다.

728x90

배경

회원가입 유저 인증 방식이 sms, email 두 가지가 있다. 기존에는 각각의 Handler에서 EmailUtilService, SmsUtilService 를 has-a 관계로 가지고 있었다. 처음 이런 클래스 구조에 대해 의문을 갖게 된 계기는 이런 관계는 테스트하기에 용이하지 않았다는 점이었다. 변경 전 구조에서 mock 테스트를 하려면 세부적인 부분(캐시 데이터 같은 것들)을 검증하지 못했다.

 

 

해결 방법 모색

  1. 각 UtilService에 인터페이스를 implements 하기 이렇게 하면 테스트에만 용이한 구조이고 다른 부분에서는 이점을 찾아볼 수 없었다. 관리 해야 할 클래스(대표적으로 인터페이스), 패키지만 늘어나고 테스트 용이성만 가져가는 구조로 판단 된다.
  2. 하나의 인터페이스 클래스만 두고 각 UtilService 들이 implements 하기 이 구조는 클래스는 안늘어나지만 유연하지 않은 구조였다. 지금은 각 UtilService 클래스들에 있는 메소드들 성격이 비슷하지만, 해당 인터페이스의 구현체의 성격이 Util인 점을 감안하면 앞으로 추가 될 메소드의 성격이 갈라질 가능성이 많을 예정이라서 유연하지 않은 구조라고 판단 된다.
  3. 기능별로 세분화 한 클래스를 UtilService에서 필요 한 기능별로 다중 구현 하기 각 UtillService가 강제적으로 오버라이딩 해야 하는 메소드가 없기 때문에 필요 없는 메소드가 없을 것이다. 그리고 클래스의 기능에 맞게 세부적인 기능을 붙일 수 있어서 유연한 구조가 될 것이고, 테스트도 용이할 것으로 판단 된다.

 

결론

3번 방법을 채택 했다. 클래스 다이어그램은 다음과 같다. 각 handler 가 필요한 기능들을 의존해서 사용할 수 있고 각각 핸들러 테스트도 용이하게 된다.

 

 

그리고 EmailUtilService에만 있는 기능은 EmailUtilService 에 해당 기능을 이중 구현 해서 필요 없는 오버라이드를 막고, 테스트 용이한 유연한 코드가 될 것으로 판단했다.

 

'난중(개발)일기 > 깨달음' 카테고리의 다른 글

Lambda SnapStart 로 회사 알림톡 람다 invoke 시간 단축  (2) 2024.09.24
Testcontainer 도입기  (0) 2024.07.06
DTO vs VO  (0) 2024.06.10
API 설계를 하며 깨달은 점  (0) 2023.01.16
Rest API에 대한 깨달음  (0) 2023.01.07
728x90

DTO는 가변이고, VO는 불변이다.

DTO 는 인스턴스, VO는 리터럴 개념이다. 그래서 DTO는 setter가 있고, VO는 없다.

 

그래서 각 객체끼리 비교할 때 단순히 equals로 비교하면 DTO는 객체의 참조값(reference value) 을  비교하게 되고 VO는 객체에 포함 되어 있는 필드값 자체를 비교(equals를 오버라이딩) 해서 객체가 같은지 알 수 있다.

728x90

회사일로 인해 나만의 홈서버 구축이 뜸했다.
 
이제 프로젝트 장기 막바지라 조금 여유가 생겨 홈서버 구축을 계속 진행할 수 있었다.
 
이번 목표는 홈서버에 웹서버 띄우기이다.
 
 

전략

우선 전략은 공인 ip로 (https는 다음 스텝으로 가져간다) 들어 오면 8080으로 포트포워딩 시켜서, 해당하는 사설 ip를 가진 pc(구축 한 홈서버)의 웹서버에 접속시키는 전략이다. 
 

 

1. 먼저 공유기에 접근 해서 포트포워딩이 이루어 져야 한다
각자 통신사에 따라 공유기 접근 방식이 다르겠다. 나의 공유기는 KT 꺼라 보통 172.30.1.254 에 접근한다. 해당 주소로 접근하면 로그인창이 뜬다. 보통 초기 접속은 아이디 : ktuser / 비밀번호 : homehub 이렇다. 
 

 

이런식으로 포트포워딩 해준다.
 
 
 

결론

이번에 직접 포트포워딩을 통해 외부 접속 작업을 해보니 예전에 학교에서 배웠던 개념들이 생각났다. (교수님들 ppt에 이미지 자료들 위주로 떠올랐다 ㅋㅋ) 정확한 개념은 이번에 정리 하면서 유익한 시간이었다.
홈서버 구축 하면서 앞으로도 여러 분야를 맛볼 수 있을 것 같아서 기대 된다.

728x90

해당 에러를 해결하기 위해서는 docker에 root 권한을 줘야한다.

 

 

sudo groupadd docker
sudo usermod -aG docker $USER

 

 

두 명령어 입력을 해준다.

 

이후

id

명령어를 입력해서

이렇게 나오면 권한이 부여 된것이다.

728x90

기본적인 흐름

  1. 서버에서 ssh-keygen -t rsa -m pem 명령어로 key pair를 생성함
  2. 특정 이름을 지어주지 않는한 id_rsa, id_rsa.pub 이렇게 두 개가 생성될 것이다. 각각 private key, public key 이다.
    public key인 id_rsa.pub을 mv id_rsa.pub authorized_key 로 등록 해준다.
    그리고 private key인 id_rsa를 mv id_rsa id_rsa.pem 으로 pem 키를 만들어준다. 만들어진 pem키는 접속할 때 필요한 키이다.
  3. ssh-add {key가 존재하는 경로와 key명} 로 해당 key를 등록해준다. 그리고 ssh-add -l 으로 잘 등록 되었는지 확인이 가능하다.
  4. ssh 로 원하는 서버에 접속한다.

 

삽질

 
1. 

WARNING: UNPROTECTED PRIVATE KEY FILE!
혹시 이런 에러 뜨면 다음 명령어 입력 하면 된다.

chmod 600 {key 명}

 
 
 
2. ssh 로 서버에 접속할 때 비밀번호입력을 요구하면 ~/.ssh/ 에 id_rsa.pem 혹은 id_rsa.pub 키가 없다는 뜻이다.
 
 
3. enter passphrase for key”가 뜨는 이유는 키 페어를 생성할 때 Phrase를 입력해서이다. 이걸 안뜨게 하고 싶으면 해당 구문을 입력하는 단계에서 그냥 엔터를 치면 된다.
 
 

느낀점

개인 클라우드 서비스를 만들어 보려고 NAS용 컴퓨터를 구입해서 이것저것 시도중이다.
이번 서버접속하는 단계를 조작해보면서 예전에 학부때 들은 시스템클라우드보안 수업에서 들은 RSA, Diffie-hellman 등 수업 내용들이 다는 아니지만 생각나고 추억들이 떠올랐다. 기분 좋은 옛 생각이 떠오르는 시간이었다.
그리고 지금껏 AWS, NCP 등 이미 만들어진 클라우드 서비스만 사용해오다가, 이런 클라우드 서비스를 직접 만들어 보니 평소에 사용하고 있던 서비스들이 어떤 방식으로 만들어 졌을거고, 해당 서비스들을 처음 개발하고 관리하는 사람들은 어떤 고민을 하면서 만들었고 유지보수 하고 있는지 더 깊게 상상해보고 고민해보는 시간이었다.
 

728x90

회사에서 꽤 크고 중요한 프로젝트를 맡았다.

 

회사에서 최초로 JPA를 도입했다. (회사에서는 Mybatis만 사용하고 있었다)

 

ORM이 확실히 객체지향적으로 코드가 깔끔하게 나오는 느낌이었다. 그런데 문제가 있었다.

 

기존에 Mybatis만 사용하고 있어서 단일로 DataSourceTransactionManager를 사용중이었는데, JPA를 도입하면서 JpaTransactionManager 를 추가 해야 했다.

 

기존에 DataSourceTransactionManagerJpaTransactionManager bean을 @Primary 로 하고 테스트 코드로 Mybatis와 JPA 혼합해서 DML을 해보았다.

    1. Mybatis로 select
    2. JPA로 insert
    1. JPA로 select
    2. Mybatis로 insert

잘 되었다.

그러나 롤백에서 문제였다.

    1. Mybatis로 select
    2. (예외 throw)
    3. JPA로 insert
  • 문제
    1. JPA로 select
    2. (예외 throw)
    3. Mybatis로 insert

Mybatis로 insert할 때 롤백이 안되는 문제였다.

 

프로젝트에 기존에 있던 로직들이 다 Mybatis로 되어 있는데, 현재 상태로는 @Primary로 되어 있어 있는 JpaTransactionManager를 사용하고 있어서 기존 로직들이 롤백이 안됐다.

 

그래서 JPA에는 JpaTransactionManager를 사용 해야 하고, Mybatis에는 DataSourceTransactionManager를 사용 하여 트랜잭션을 관리 하게끔 해야 했다.

 

여러 대안이 있었다.

  • ChainedTransactionManager
  • JtaTransactionManager
  • @Primary를 DataSourceTransactionManager 로 변경하고 JPA 로직에만 @Transactional 에 트랜잭션 매니저 명시

ChainedTransactionManager는 후 순위로 커밋하는 트랜잭션에서는 예외가 발생하면 이전 트랜잭션에서 커밋된 부분은 롤백이 안되는 한계가 있었고, 어느 한 쪽의 커넥션 갯수가 부족해서 타임아웃이 발생할 가능성도 우려 됐다. 무엇보다 해당 트랜잭션 매니저는 단순 순차적으로 커밋하는 것일 뿐 2PC가 아니라 도입하기 걱정스러웠다.

 

JtaTransactionManager는 XA 프로토콜을 사용한 완벽한 2PC로 구현되어 있었다. 그러나 락이 잡히는 시간이 길어질 것 같고, 여러번 디비랑 커넥션을 맺어 퍼포먼스에 이슈가 있을 것 같았다.

 

그래서 @Primary를 DataSourceTransactionManager 로 변경하고 JPA 로직에만 @Transactional 에 트랜잭션 매니저 명시를 해서 처리했다.

 

 

마무리

이번 이슈로 더 성장한 느낌이 들어 좋다. 

 

고민하는 스펙트럼이 넓어졌다.

+ Recent posts