GopherCon Korea 2023
Scenario Test
김재원 @ AB180 & Airbridge
인수 테스트 자동화로 자신감, 생산성 높이기
GopherCon Korea 2023
Data Pipeline Team Lead
[email protected]
https://abit.ly/ab180-scenario-test
Slide 2
Slide 2 text
GopherCon Korea 2023
Index
● 배경 소개
● Scenario Test
● Lessons Learned
● What’s Next
● Q&A
Slide 3
Slide 3 text
GopherCon Korea 2023
배경 소개
Slide 4
Slide 4 text
GopherCon Korea 2023
Airbridge
Daily 20억건 이상의 사용자 행동 데이터 수집 및 분석
People-Based Attribution
Slide 5
Slide 5 text
GopherCon Korea 2023
Airbridge Data Pipeline
https://engineering.ab180.co/stories/kafka-consumer-proxy
Slide 6
Slide 6 text
GopherCon Korea 2023
Airbridge Data Pipeline
1) WAS 마이그레이션: Python -> Go
2) Worker 마이그레이션: Python -> Rust
3) DB 마이그레이션: DynamoDB -> ScyllaDB
4) 네트워크 비용 최적화
비용 최적화 액션 아이템
Slide 7
Slide 7 text
GopherCon Korea 2023
Airbridge Data Pipeline
1) WAS 마이그레이션: Python -> Go
https://abit.ly/ab180-go-migration
Slide 8
Slide 8 text
GopherCon Korea 2023
Airbridge Data Pipeline
2) Worker 마이그레이션: Python -> Rust
AS-IS:
TO-BE:
- 성능이 매우 중요한 부분에
Rust 도입 및 분리를 결정함
- Preprocessing 로직이 매우 복잡한데
- 이 복잡한 걸 Python, Rust로
둘 다 유지보수 해야하는 상황
Slide 9
Slide 9 text
GopherCon Korea 2023
Airbridge Data Pipeline
2) Worker 마이그레이션: Python -> Rust
TO-BE:
- 성능이 매우 중요한 부분에
Rust 도입 및 분리를 결정함
- Preprocessing 로직이 매우 복잡한데
- 이 복잡한 걸 Python, Rust로
둘 다 유지보수 해야하는 상황
Slide 10
Slide 10 text
GopherCon Korea 2023
Airbridge Data Pipeline
2) Worker 마이그레이션: Python -> Rust
TO-BE1:
TO-BE2:
Preprocessor를 Golang으로 만들어서 전처리를 한 데이터를 따로 만들기로 결정!
Slide 11
Slide 11 text
GopherCon Korea 2023
1. Touchpoint Worker는 성능이 매우x100 중요해서 Rust로 결정
2. Preprocessor는 성능도 중요하긴 하지만 유지보수 가능 여부가
훨씬x100 중요해서 Golang으로 결정
Airbridge Data Pipeline
2) Worker 마이그레이션: Python -> Rust
Slide 12
Slide 12 text
GopherCon Korea 2023
마이그레이션 검증은 어떻게?
1. 단위 테스트는 최대한 옮긴다
2. 필요한 통합 테스트 또한 옮긴다
3. 인수 테스트는 전부+ 더 만들어서 옮긴다
- 기존 Python 코드로 일주일치 테스트 케이스를 만듦
- 확신을 매우 높여줌
Slide 13
Slide 13 text
GopherCon Korea 2023
인수 테스트 (Acceptance Test)
Slide 14
Slide 14 text
GopherCon Korea 2023
인수 테스트 (Acceptance Test)
- Given: 어떤 환경이 주어졌을 때
- When: 어떤 요청사항이 들어온다면
- Then: 이런 결과가 나오길 기대한다
Slide 15
Slide 15 text
GopherCon Korea 2023
기존 인수 테스트의 문제
1. 매번 짜야하는 코드가 많음
- 그래서 잘 안짜게 되거나 생산성이 떨어짐
- Framework이 없다면 각자 다른 스타일로 짜게 됨
- 규칙을 정할 순 있겠으나, 강제하기 어려움
2. 어떤 인수테스트가 있는지 한 눈에 보기 어려움
- 이 서비스에는 어떤 기능적인 요구사항을 만족하고 있을까?
3. 언어 변경을 하게 된다면 사람이 손으로 옮겨줘야 함
- 언어를 변경할 일은 잘 없으니 참을만 함
Slide 16
Slide 16 text
GopherCon Korea 2023
Scenario Test
Slide 17
Slide 17 text
GopherCon Korea 2023
Scenario Test
자동화된 인수 테스트
Scenario: “json 형태”의 인수 테스트 케이스 하나
- (Given) Context: 이 데이터를 처리하기 위해 필요한
외부 데이터 (RDB 데이터, OS 환경변수 등)
- (When) Input: 실제 Component의 Input
- (Then) Output: 기대하는 Output
(Kafka로 보내야하는 데이터, Cache에 써야하는 데이터 등)
my_scenario.json
Slide 18
Slide 18 text
GopherCon Korea 2023
Scenario Test
자동화된 인수 테스트
Slide 19
Slide 19 text
GopherCon Korea 2023
my_scenario.json
Scenario Test
Scenario Generator
Slide 20
Slide 20 text
GopherCon Korea 2023
my_scenario.json
Scenario Test
Scenario Tester
Slide 21
Slide 21 text
GopherCon Korea 2023
Scenario Test
Scenario Tester - 결과
Slide 22
Slide 22 text
GopherCon Korea 2023
Scenario Test
1. 기존 Python 코드로 일주일치 Scenario를 만듦
- Kafka -> Generator(on ECS) -> Kafka 구조로 생성
- 100억 개가 넘는 케이스를 생성
- 장애 방지를 위해 Scenario Generator 전용 Infra를 다 새로 띄워 격리시킴
2. 만들어진 Scenario로 Golang 코드 검증
- Kafka -> Tester(on ECS) -> Kafka 구조로 테스트
- 실패한 케이스만 모아두고 로컬에서 검증
- 코드 다 고쳤으면 다시 검증
마이그레이션 과정에서의 활용
Slide 23
Slide 23 text
GopherCon Korea 2023
Scenario Test
1. encoding/json 패키지에서 생긴 이슈
- “<”와 “>”를 unicode escaping 해버림
단위 테스트로는 못잡았던 케이스
“>” 를 “\u003e”로 바꿔버리는 모습.. 🤦
Slide 24
Slide 24 text
GopherCon Korea 2023
Scenario Test
1. encoding/json 패키지에서 생긴 이슈
- “<”와 “>”를 unicode escaping 해버림
단위 테스트로는 못잡았던 케이스
Marshal을 이런 식으로 해서 해결 (별로 행복하진 않음)
Slide 25
Slide 25 text
GopherCon Korea 2023
Scenario Test
1. encoding/json 패키지에서 생긴 이슈
- “<”와 “>”를 unicode escaping 해버림
- json.Unmarshal을 할 때 case-insensitive하게 받음
단위 테스트로는 못잡았던 케이스
key를 “lowercase_key”로 정의했으나 “LOWERCASE_KEY”도 받아주는 모습.. 🤦
Slide 26
Slide 26 text
GopherCon Korea 2023
Scenario Test
1. encoding/json 패키지에서 생긴 이슈
- “<”와 “>”를 unicode escaping 해버림
- json.Unmarshal을 할 때 case-insensitive하게 받음
단위 테스트로는 못잡았던 케이스
<해결>
A. Case Sensitivity가 중요하지 않은 struct(정해진 값만 들어오는 것이 확정)는 Known-issue로 처리
B. Case Sensitivity가 중요한 struct는 정해진 Key만 받도록 Unmarshal을 직접 구현
(별로 행복하지 않음... 😭 더 좋은 방법/라이브러리가 있다면 알려주세요)
Slide 27
Slide 27 text
GopherCon Korea 2023
Scenario Test
1. encoding/json 패키지에서 생긴 이슈
- “<”와 “>”를 unicode escaping 해버림
- json.Unmarshal을 할 때 case insensitive하게 받음
2. 그외 미처 생각도 못했던 곳에서 발생했던 실수
- Timestamp를 13자리(ms)로 기록해야하는데 10자리(sec)로 기록함
- 잘못된 hash 알고리즘으로 hashing을 함
단위 테스트로는 못잡았던 케이스
(기억은 다 안나지만, 이거 보다 훨씬 많은 케이스가 있었습니다)
Slide 28
Slide 28 text
GopherCon Korea 2023
Scenario Test
1. Dependency Injection이 쉬운 구조로 코드를 짜놔야 편하다
- Mock Infra로 쉽게 교체할 수 있어야 Generator/Tester를 만들기 편함
2. 활용한 도구
- https://github.com/yudai/gojsondiff
- Diff가 생겼을 때 예쁘게 볼 수 있음
- https://pkg.go.dev/gorm.io/gorm/callbacks#AfterQuery
- gorm AfterQuery Callback을 통해 Context를 capture할 수 있음
3. stdin, stdout(+ Makefile)을 잘 활용하면 쉽게 생성/테스트 할 수 있다
만들때 팁
Slide 29
Slide 29 text
GopherCon Korea 2023
Scenario Test
만들때 팁
https://github.com/yudai/gojsondiff
어떤 테스트 케이스에서 어떤 Diff가 생겼는지
시각화 해주어서 디버깅 하기 아주 좋음 👍
(빨간색은 원래 기대한 결과, 초록색은 현재 결과)
Slide 30
Slide 30 text
GopherCon Korea 2023
Scenario Test
만들때 팁
stdin, stdout으로 Scenario 만들기 (자유도가 높고 쉬움)
Slide 31
Slide 31 text
GopherCon Korea 2023
Scenario Test
1. 개발 공수가 많이 든다
- 고도화를 하다보니, 서비스 하나를 만드는 수준의 공수가 들었음
- 언어마다 다 각자 개발해줘야 함 (Python, Go, Rust)
2. Scenario Generator/Tester를 잘못 만들면 말짱 도루묵
- 일반적으로 짜는 테스트 코드 보다 복잡하기 때문에 실수하기 더 쉬움
단점
Slide 32
Slide 32 text
GopherCon Korea 2023
Scenario Test
- 기존 Python Scenario Generator에서 Scenario를 잘못 만듦
- Input Data를 코드에서 다루다가 변경해버림
- A -> A’ 로 바뀐 상태로 Scenario가 저장됨
- A’를 Input으로 받는 코드를 작성해서 Golang으로 신나게 검증을 함 🥲
겪었던 장애 - Scenario를 잘못 만든 케이스
Input Data는 불변성을 보장할 수 있도록 장치 추가
Slide 33
Slide 33 text
GopherCon Korea 2023
1. 한 번의 큰 장애 🥲 (및 성장)
2. 15배 효율적인 이벤트 처리 (월 천 만원 이상의 비용 절감 효과 🤑)
3. Python -> Go
- 강타입 언어라 유지보수 하기 훨씬 용이함 👍
- 훨씬 좋은 성능 👍
- 더 확장성 있는 구조로 변경됨 👍
마이그레이션 결과
Slide 34
Slide 34 text
GopherCon Korea 2023
Lessons Learned
Slide 35
Slide 35 text
GopherCon Korea 2023
Lessons Learned
● 마이그레이션을 하면서 겸사겸사 리팩토링을 하면 오히려 안좋다
○ 매몰작업이 될 수 있더라도 미리 리팩토링을 하고, 같은 구조로 옮겨야
검증이 쉬움
● 일정은 더 넉넉히 잡아야 한다 (자기 자신을 과대평가 하지 말자)
○ 처음에 2개월 정도 걸릴줄 알았으나 4개월 걸림
● 리팩토링을 이제 정말 자신있게 할 수 있게 되었다
○ 레거시를 다시 짜면서 성능 뿐만 아니라 오히려 생산성도 올라감
○ 하지만 이때 제일 조심해야됨
Slide 36
Slide 36 text
GopherCon Korea 2023
What’s Next
Slide 37
Slide 37 text
GopherCon Korea 2023
What’s Next
1. 여러 컴포넌트에 걸친 테스트를 할 수 있게 하기
- A Service의 Output을 B Service의 Input으로 씀
- 한 서비스의 시나리오 파일은 Downstream 서비스들에 다 포함돼야 함
2. 언어에 구애받지 않는 단일화된 Generator/Tester 만들기
- 현재는 언어마다 각자의 Generator/Tester가 있음
- 관리도 힘들고 사용법도 각자 조금씩 다름
- 잘 만들어서 Open Source화를 할 수 있으면 좋지 않을까
3. 비기능적 요구사항도 테스트
Slide 38
Slide 38 text
GopherCon Korea 2023
Q & A
Slide 39
Slide 39 text
GopherCon Korea 2023
Thank you
https://recruit.ab180.co/