🚀 94sssh
Published on

2024.05.09

[우아한 개발] - 03. 백엔드 개발자로 성장하기

개발자 머피의 법칙

사용자의 입력은 UI에서 먼저 검증해 편리성을 높이고 서버 측에서 올바른 범위의 값을 입력했는지 무조건 검증해야 한다.

사용자의 입력은 무조건 검증

  • 폼 값을 수정해서 서버에 요청을 전달한 예 - 사용자의 입력 유효성 검사는 항상 서버에서 해야 하며, 프론트에서 계산 로직을 검사했더라도 최종 결과는 서버에서 재계산해야 한다.
  • 의도치 않게 모든 조회 조건이 사라지게 해선 안 되고, 조회 조건을 생성하는 사용자 요청 데이터는 무조건 서버 측 검증을 거친다.

성능 측정 없는 캐시 사용은 성능을 저하시킬 수 있다

캐시가 만능일까요? 면접 단골 질문 중 하나, 프론트에서도 메모이제이션 등과 관련하여 캐시의 장단점을 많이 묻곤 하는데, 역시 좋은 만큼, 모르고 썼다면 더욱 독이 될 수도 있다고 생각한다.

원격 분산 캐시는 네트워크 대역폭을 충분히 확보해야 한다. 직렬화 시 데이터 포맷의 변경 체크 문제도 있는데, 필드의 변경은 직렬화된 데이터의 변경으로, 캐시되고 있는 데이터에 필드를 추가하거나 필드의 이름을 바꾸면서, 배포되기 전 서버에서 캐싱한 데이터와 배포 중인 서버에서 캐싱한 데이터가 서로 다른 필드를 가지고 있게 되어 둘 중 한 곳에서 특정 필드의 데이터를 누락시키게 된다.

  • 네트워크 대역폭이 충분한지 성능 테스트 필수
  • 직렬화/역직렬화를 할 때 충분한 성능이 확보되는지 성능 테스트 필수
  • 원격 캐시 대상 데이터 필드는 DB의 컬럼처럼 조심스럽게 리팩터링, 혹은 원격 캐시 대신 로컬 캐시 사용 - 이 경우 데이터 동기화 문제 해결 필요
  • 캐시가 아니라 이미지 파일 용량이 큰 경우 대규모 이벤트시 대역폭을 많이 차지해 성능이 현저하게 저하, 이벤트 랜딩 페이지는 이미지 크기를 최적화하고, CDN 사용 등으로 대역폭 문제가 발생하지 않도록 조치

인증과 권한은 다르다
인증권한은 다르게 체크해야 함. 로그인 여부를 검사하는 것과 그 사용자의 ID가 요청 URL의 사용자와 동일인인지 여부를 검사하는 것은 다르다. 인증만 하고 내부 API 서버로 요청을 바이패스하면 절대 안 되며 권한 검사까지 반드시 해야 한다. 프론트엔드 서버에서는 애초에 요청 매개변수를 받지 않고, 로그인 사용자의 정보를 인증 세션에서 읽어서 바로 데이터를 읽어야 한다.

사용자의 로그인 실패 횟수를 트래킹
사용자 계정에 대한 무차별 대입 공격으로 서버가 마비될 지경, 쿠키 등의 클라이언트 측 저장소를 사용하면 공격자가 값을 조작해서 반복 공격이 가능. 사용자 입력 검증은 서버에서 해야 한다.

사용자의 가상 재화는 별도 결제수단처럼 독립 인증해야 한다
사용자의 가상 재화를 실제 사용자 본인인지 여부 검증 없이 우회 수단으로 현금 등으로 전환 가능한 때에는 가상 재화도 다른 결제수단과 동일하게 별도 인증 절차를 거치도록 해야 한다. 포인트를 사용하는 경우에도 로그인 비밀번호와는 다른 비밀번호를 입력 받고, 3회 이상 실패 시 결제 불가하도록 처리하고, 알림을 보내는 등으로 처리

직원 PC에서 운영 서버 API, DB 저장소 접근을 통제

  • 운영 DB에 접근하는 계정은 통제된 몇 명(DBA)이 아니면 DDL(가급적 DML까지) 권한을 제거한다.
  • 로컬 개발 환경에서 production 프로필로 서버를 사용 - 데이터가 서버 / 애플리케이션으로 전달 되지 못하고 로컬 환경의 서버로 전송 되어 버림
  • 모든 서버는 프라이빗 망을 통해, 꼭 필요한 것만 프록시를 통해 퍼블릭에서 접근, 필요한 특정 직원을 기준으로, 되도록 쓰기 없이 읽기 권한으로 통제

중요 배치는 실행 여부를 제3의 시스템에서 검증

로그를 외부 서버로 수집하는 것은 별도 프로세스에서 비동기로

  • 좋은 로그 수집을 위해 애플리케이션 프로세스와 독립된 로그 수집/전송 데몬을 사용해 로그를 비동기로 CPU와 디스크/네트워크 IO에 영향을 최소화하는 방식으로 차등 전송
  • 애플리케이션 자체에서도 비동기로 디스크 쓰기 작업을 하도록 설정
  • 애플리케이션 자체에서 처리하려고 했다면 성능 테스트를 한다.

기본 키는 Integer가 아니라 Long으로

오류 로그는 일상적인 것과 크리티컬한 것을 구분
중요도를 인지할 수 있도록 구분한다.

롤백 가능한 배포

SpoF를 제거하자
'설마 내가 구축한 서버가 죽겠어?' => 죽는다.
그렇기 때문에 안 죽는 서버를 만드는 게 아니라 '죽어도 괜찮은 서버를 구축해야 한다.

메인 데이터베이스 IDC 탈출 성공기

하나의 메인 데이터베이스에 모든 데이터와 로직을 집중시키는 모노리틱 아키텍처방식을 선택했지만 시간이 지나며 상상을 초월할 정도로 넓은 수정할 개발 범위, 복잡한 로직과 구조 때문에 장애 상황에 늦은 대응 등 문제가 발생, 이를 타개하기 위해 데이터베이스를 클라우드 환경으로 운영하고자 하였음.
3년이 넘는 시간 동안 메인 데이터베이스 탈출 프로젝트를 통해 서비스 영향도를 줄여나가 결국 메인 데이터베이스로 유입되던 모든 커넥션을 제거. 하나의 큰 데이터베이스는 100개 이상의 데이터베이스로 분리됨.

문제점

  • 서비스가 종료되거나 아키텍처 개선 등으로 리팩터링 되었지만, 레거시를 정리하지 않아 프로시저 내부 로직이 남아 있는 문제. 데이터 누수가 생기거나 병목 현상이 발생하는 등 레거시를 정리하지 못해 부작용이 발생
  • 명명 규칙에 대한 협의 없이 여러 명이 동시다발적으로 개발/운영을 하다보니 이름만으로 목적을 알 수 없는 상황 발생
  • 여러 개발자가 사용하지만 아무도 오너십을 가지지 않는 상황이 되어 전체 로직 확인 없이 각자 필요한 부분만 조금씩 수정하다 다른 부분에서 어떤 문제를 야기할 수 있는지 파악이 힘들어지고 전체 시스템에 대한 불확실성이 증가
  • 중복 코드가 많이 발생

배운 점

  • 프로젝트 계획시 레거시 제거를 범위에 포함하자
  • 명명 규칙을 미리 정하고 공유하자
  • 코드에 대한 오너십을 명확하게 정하자
  • 단순하고 명료한 로직을 사용하자, 기조 코드를 재사용한다면 불필요한 로직이 수행되지는 않는지 등 점검을 한다.

메시지 발송 이중화 여정기

외부 시스템 장애 발생 시 복구를 기다릴 수 밖에 없는 환경 => 이중화 작업 시작
라우터 모듈에서 외부 시스템별 트래픽 비율 정보를 알고 있고, 비율에 따라 A와 B 외부 시스템으로 트래픽을 분산하도록 아키텍처를 결정

이중화 구현을 통해 장애가 발생시 인지와 동시에 외부 시스템으로 트래픽을 전환해 안정적으로 장애 상황에 대응 및 공유가 가능해짐.