네이버 인턴 과정 중 배운 것들
스스로 고민했던 것들, 질문했던 것들, 받았던 조언과 팁
1. 스스로 고민했던 것들
1-1. 기사 내용에 대한 캐시 데이터에도 TTL을 꼭 달아줘야 할까?
- 서비스 운영 측면에서 캐시를 사용하는 이유를 다시 생각해보자. 캐시는 짧은 단위시간동안 많은 cache hit를 올리면 성공한 거.
- 단위시간을 길게 잡아도 평상시의 보통 작업에서는 큰 문제가 없을지도 모르나, 돌발적으로 단위시간이 채 끝나기 전에 기사 데이터가 삭제되거나 업데이트될 수 있다. 이럴 경우 남은 단위시간이 끝날 때까지 변경된 내용이 사용자에게 반영되지 않는다.
- 그래서 캐시를 할 때 TTL 주기, 저장소 용량, 원본 데이터의 업데이트 빈도를 충분히 고려해야 한다.
1-2. 백엔드 튜닝
- 안정성 → 죽지 않는 서비스
- 부하 테스트
- 이 서비스의 일간 PV는 몇이고, 최대 몇 tps까지 버틸 수 있을까?
- 몇 tps까지 버틸 수 있으면 되는지, 왜 그만큼만 해도 충분한지?
- 익셉션 핸들링
- validation 체크
- 기사 JSON 파싱하는 부분에서 제일 많이 고민해야 함
- 파라미터? 값?이 한두개 빠져있거나 유효하지 않은 값이 들어있으면 어떻게 하지?
- JSON 포맷이 변경되면 어떻게 대응하지?
- 정해진 용량?을 초과하는 데이터가 들어오면 어떻게 처리하지? → 애초에 그럴 수가 있을까?
- 외부 기사 Notification API 장애 체크
- 주기적으로 헬스 체크를 해서 API가 잘 살아있는지 체크
- 만약 API가 죽어서 기사가 계속 안 들어오고 있다면, API가 고쳐질 때까지 어떻게 서비스를 하지?
- 헬스 체크 말고 좀 더 적극적으로 장애를 대비할 수 있는 방법이 없을까?
- 아키텍쳐
- 장비를 낭비하지 않으면서 효율적인 운영을 하려면 어떻게 백엔드를 구성해야 하지?
- redis를 꼭 둬야 하나? 장비 이중화/다중화는 어디까지?
- 제한적인 리소스에서 최대한의 효율을 어떻게 뽑아낼까?
- 각 레이어별 장애 대응 방향 (일단 처음에는 전부 1대씩이라고 가정, 아래 문제를 풀어나가면서 다중화 방향 고민)
- API 장애 → 위에 API 장애 체크 부분에서 해결
- Web Server 장애 → 정적 리소스 트래픽이 전부 WAS로 몰림, 빠른 failover 필요?
- WAS 장애 → Web Server가 혼자 서비스를 해나가야 함, DB 접근 불가
- Redis 장애 → 트래픽이 전부 MariaDB로 몰림, 캐시 활용 불가
- MariaDB 장애 → Redis가 임시로 캐시 데이터를 활용해 서비스를 해나가야 함
- 성능 향상 → 빠른 서비스
- 비즈니스 로직 최적화
- 각 메소드의 처리 상황을 보고 병목 구간 찾기
- 쿼리 최적화
- 웹 서버 최적화
1-3. 실제 장애 대응
- 12/12 날씨 서비스 장애 발생, 원인은 갑작스러운 재난문자로 인한 트래픽 폭등 → nginx 죽음 (502 error)
- 옆에서 주워들은 바를 토대로, 장애가 났을 때 어떻게 복구하는지와 사전에 장애 상황을 대비해 해두어야 할 것들을 정리
- 장애 발생시 긴급 복구
- 서버를 재배포하면 다시 살아나지만, 문제는 전체 서버를 한 번에 다 내렸다가 다시 올릴 수도 없으니 부분부분 재배포를 진행하는데, 몇개 먼저 살려놓으면 그쪽으로 트래픽이 확 몰려서 금방 다시 죽어버리는 악순환이 반복됨
- 이번 날씨 서비스 장애는
weather.naver.com
에 트래픽이 몰렸던 건데, 빨리 재배포할 동안만 임시로 검색쪽으로 유입을 우회할 수도 있지 않을까 → 어떻게 보면 책임 전가, 검색쪽에서 이만큼의 트래픽을 대신 버텨주고 있어야 함
- 최후의 방법으로, 스케일 아웃을 하면 해결이 됨 (인프라 팀에서 고생..!)
- 장애 상황을 대비해 해두어야 할 것들
- 모니터링 알림 계속 받기 → 알림 울리는거 싫다고 꺼뒀다가 정말 큰일나는 상황에 바로 대응 못 할 수도 있음
- 40x, 50x 에러들에 대해 미리 에러 페이지 만들어두기 → 에러 코드, 에러 메시지, 에러가 발생한 부분의 코드 등이 그대로 노출되는 것은 최악
- 당연한 이야기지만, 사전에 트래픽이 튈 경우를 대비한 플랜을 갖춰두고 있어야 함
2. 질문했던 것들
- 쿠키가 커지면 HTTP header 용량이 커지니까 더 느려진다고 알고 있는데, 쿠키 사용이 불가피하다면 어떻게 최적화하나요?
- HTTP 1.1과 2.0의 차이점에 대해서 정확히 숙지하자. 힌트로는 HTTP 2.0에서 header 압축과 관련된 설정을 할 수 있다.
- 브라우저가 HTTP 2.0을 지원하지 않으면 그냥 HTTP 1.1로 돌리면 될까요?
- log 데이터는 차지하는 용량에 비해 의미있는 데이터가 적을 것 같은데, 어떻게 쌓아두고 관리하는지 궁금합니다.
- log 특성에 따라 삭제하는 주기나 기준을 다르게 적용한다.
- 만약 이 개발 과제에서 사용자의 access log를 저장한다고 하면, log의 만료 기한을 둬서 어떤 사용자 식별 정보로 1달정도 새로운 log가 쌓이지 않았을 때 해당 사용자는 없는 사용자라 간주하고 log를 삭제할 수도 있고.
- 아니면 텍스트 압축을 해서 더 오래 저장할 수도 있다. 텍스트 데이터는 60~80%정도 압축할 수 있으니 용량을 많이 줄일 수 있다. (참고
- log를 저장하는 기준을 어떻게 잡아야 할까요?
- 서비스마다 케바케. 보통 하루동안의 PV, UV 정보를 바탕으로 저장할 log 용량을 결정한다. 이는 데이터를 쌓는 기준에 따라 달라질 수 있다.
- Spring의 개방 폐쇄 원칙(Open-Closed Principle)을 어느 정도까지 지켜야 하나요? 프로젝트 규모가 작거나 로직이 간단한 경우에는 OCP를 철저하게 지키려고 하면 오히려 가독성이나 생산성이 떨어지는 것 같습니다.
- 지금부터 습관을 잘 들여놓는 것이 좋다. 앞으로 다른 여러 개발자들과 같이 개발하고, 어떤 프로젝트에 중간에 투입될 수도 있는데 그런 상황에서 처음 시작을 확장성 높게 만들어두지 않으면 나중에 매우 힘들어진다. 괜히 있는 원칙이 아니니 연습하고 익숙해지자.
- 기사 수신을
~/create/{oid}/{aid}
~/update/{oid}/{aid}
~/delete/{oid}/{aid}
로 받는 대신, HTTP 메소드를 GET
POST
DELETE
로 받으면 url에 /{oid}/{aid}
만 있어도 되지 않을까요?
- 현업에서 RESTful하게 만들기 까다로운 경우가 종종 있다.
- 예를 들어, 하나의 URL에 여러 개의 데이터 정보가 한꺼번에 들어오는 경우가 있다. URL에
[XXX, YYY, ZZZ]
같은 형식이 들어올 수도 있고, GET 요청에 Response Body에 덩어리로 담겨져 올 수도 있다. 이렇게 여러 개의 데이터를 한번에 전달하는 방식에는 딱 정답이 있는 건 아니다.
- 엄청나게 많은 데이터를 한 번에 처리하는 batch 작업에서는
@Transactional
을 거는 게 좋을지 나쁠지 궁금합니다.
- 케바케. 만약 데이터에 이상한 값이 들어가면 안 되는 조건이라면 그냥 작업을 잘게 쪼개서 각각 트랜잭션 걸어주는 게 낫지 않을까.
- 트랜잭션의 Isolation Level을 어떻게 정해야 할지 기준을 잘 모르겠습니다.
- 반드시 하나의 정해진 답이 있는 건 아니다. isolation을 하는 의미와 트래픽 상황, 데이터 성격에 따라 케바케.
- 보통 실제 서비스에서는 Create, Update, Delete는 WAS에서, Read는 웹 서버에서 처리하므로 그렇게 복잡하게 고민하지는 않아도 된다.
- 추가로, 과거에는 Delete를 거의 하지 않았었다. 데이터를 지우는 대신
deleted
와 같은 플래그를 달아뒀다.
- 보통 Read를 최우선시 한다. 어짜피 중간에 캐시가 또 껴있으니까 Update가 항상 바로 반영되지 않을 수 있고, 반드시 바로 반영되어야 할 필요도 없다. 나중에 반영된 걸 보여주면 된다. 다만, 네이버 페이같이 절대 그러면 안되는 예외 상황도 있다.
- 사실 실제 서비스에서는 DB를 매번 읽어서 서비스하는게 아니라서, DB 최적화를 한다고 해도 그 효과가 그렇게 막 드라마틱하지는 않을 수 있다.
- 예외 종류가 많은데 일일이 대응 코드를 만드는게 맞는지, 예외 핸들링은 어떻게 하면 좋은지 감이 안 잡힙니다.
- 서버 에러는 RuntimeException으로 잡고, HTTP response code 확인해서 200이 아닌 것들은 전부 예외 처리한다.
- 400대, 500대 에러는 각각 경우에 따라 응답을 다르게 설정해준다.
- API 에러는 커스터마이징한 exception handler 만들어서 잡는다.
3. 조언과 팁
- 가장 안 좋은 건 이유 없이 그냥 쓰는 것. 어떤 요소를 쓴다면 왜 쓰는지, 기술적으로 어떤 이점이 있는지 (구체적인 수치와 함께), 이외에 다른 대안은 없는지 등에 대해 자세하고 명확한 이유가 있어야 한다.
- 명확한 단위에 대한 감이 있어야 한다. TPS, OPS와 같은 수치에 익숙해지도록 하자
- 프로젝트 설계할 때에는 가능한 모든 케이스를 고려해서 촘촘하게 설계해야 한다. Best case보다는 Worst case부터 발생할 수 있는 모든 예외 상황을 고려하는 방향으로 가야 한다.
- 뻔한 구조도는 그리나 마나. 구체적으로 각 파트마다 수행하는 작업과 작업의 흐름이 보이도록 프로젝트 구조를 짜야 한다.
- 새로운 걸 익힐 때에는 공식 문서를 먼저 보자.
- 프레임워크가 제공하는 기능에 대해 얼마나 많이 잘 알고 잘 써먹을 줄 아는지가 생산성에 큰 영향을 미친다. 먼저 잘 쓰는 방법을 익히고, 어떻게 이게 만들어졌고 왜 이런 방향으로 발전했는지 고민.
- 스터디 문서를 작성할 때에는 책에 있는 내용처럼 원론적인 걸 기록하고 끝나지 말고, 실제로 이걸 이용해 개발했을 때의 이야기를 좀 더 넣자.
- 사소하다고 생각할 수 있지만, 코딩 컨벤션 꼭 유념하자.