GN⁺ 8달전 | parent | ★ favorite | on: 좋은 시스템 설계(seangoedecke.com)
Hacker News 의견
  • 나는 이 부분에 종종 혼자임을 느낌. 엔지니어들은 복잡한 시스템을 보면 흥미로운 요소가 많아서 “여기서 진짜 시스템 설계가 이뤄지고 있음!”이라고 생각하지만, 사실 복잡한 시스템은 좋은 설계가 부재한 결과인 경우가 많음. 구직자라면, 이 사실을 면접 중에는 완전히 잊어야 함. 나도 시스템 설계 면접에서 이런 생각을 솔직하게 전달했다가 실수한 경험이 있음. 가상의 스타트업 앱 면접에서 “이 정도 QPS에선 backpressure는 무관심해도 됨”, “cron job 대신 큐를 쓸 필요가 없음, 물론 트레이드오프는 있음”, “SQL vs NoSQL? 팀이 가장 잘 아는 걸 쓰면 됨” 같은 답을 했지만, 면접관들은 이런 답을 원하지 않음. 하얀 보드판을 가득 채우고 Kubernetes가 Kubernetes를 관리하는 수준으로 복잡한 설계를 보여줘야 그들이 원하는 신호를 줄 수 있음

    • 내가 수백 번 시스템 설계 면접을 봤고 여러 명을 트레이닝한 입장에서 말해봄. 네가 언급한 답변은 신호가 약함(큐 관련 답변은 예외), 면접관들이 진짜 알고 싶은 건 ‘왜’ 이런 결정을 내렸는지, 어떤 요소를 고려했는지, 네 사고과정을 듣고 싶어함. 답에 대해 자세히 설명하지 않으면, 면접관 입장에선 “얻을 정보가 별로 없다”라고 생각하기 쉬움. 그래서 면접자는 면접관이 원하는 정보를 적극적으로 전달해야 함. 또 좋은 면접관이라도 답을 억지로 끌어내야 한다면 “설명은 합리적이지만 커뮤니케이션이 비효율적임”이라고 메모할 것임. 커뮤니케이션 스킬도 평가 대상임. 마지막으로 SQL/NoSQL 답변은 동의하지 않음. 팀 경험도 중요하지만, 기술 간의 차이가 명확하고 상황에 따라 성능 차이도 큼. 해당 답은 다양한 상황 경험이 부족해 보인다는 인상을 남김

    • “면접은 양방향”이라는 말처럼, 네 답변들은 매우 합리적이라고 생각함. 내가 면접관이었다면 오히려 높은 점수를 줬을 것임. 반면 네 답변으로 불합격시키는 회사라면 오히려 그 회사가 별로일 가능성이 높음. 하지만 현실적으로는 빨리 자리를 잡아야 하는 경우가 많으니, 균형을 잘 맞춰서 상대가 원하는 쪽으로 답을 맞추는 것도 필요함

    • 이 조언은 좋지 않음. 단순하면서도 우아한 설계는 잠재적 문제를 무시하는 것에서 출발하지 않음. 추궁 질문은 기술 트리비아 쏟아내는 시간이 아니라 서로 논의하자는 신호임. 네 답변들은 현명함을 보여주지 못하고, 아직 미성숙하다는 인상을 줌. 면접관 탓이 아님

    • 옆 댓글이 지적한 “면접은 양방향”이라는 지점에 공감하지만, 좋은 면접관은 “이 답도 좋은데, 나는 지금 이 테마에서 지식을 테스트하는 중임”이라고 솔직히 말해줄 것임. 본인이 엉뚱한 얘기만 계속하는 건 오히려 불안 신호임

    • LinkedIn-driven development가 왜 존재하는지 그대로 보여주는 예시라고 생각함. 수많은 기술을 CV에 나열하는 게, 하나의 Postgres와 모듈형 모놀리스만 잘 썼다고 설명하는 것보다 훨씬 더 멋져 보이는 게 현실임

  • 정말 좋은 글이라는 생각임. 하지만 이런 베스트 프랙티스에 대해 한계도 언급하고 싶음. 예를 들어, “서로 다른 5개 서비스가 한 테이블에 쓰지 말고, 4개가 API 호출하거나 이벤트를 던지도록 하고, 1개 서비스만 테이블에 쓰기”라는 조언이 있는데, 현실은 그렇게 깔끔하게 나뉘지 않음. 다섯 모두가 DB에 접근하면 이미 분산 시스템을 만들고 있는 셈인데, DB가 기본적으로 권한·트랜잭션·커스텀 쿼리를 지원하니 별도 인터페이스 설계가 필요 없기도 함. 반면, 한 서비스로 높은 수준 인터페이스를 만들면, 이제는 별도의 인증·트랜잭션·예외처리까지 직접 구현해야 함. 실질적으로는 장애모드와 복잡한 마이크로서비스 관리세가 더 생기는 것 아닌가라는 의문임. 한편, 여러 서비스가 한 DB 접근한다는 자체가 코드 스멜일 수도 있음. 아마 이 DB가 여러 개가 합쳐진 흔적일 수 있고, 서비스도 실은 두세 개로 줄일 수 있을지도 모름

    • “무엇을 얻냐”는 질문에 대해, API는 공유 DB 스키마를 사용하는 것보다 변화 적응성이 훨씬 높음. 여러 시스템을 일해본 결과, 한 DB를 여러 서비스가 공유하는 구조는 다시 설계하지 않을 것임. 2000년대 초 소규모 회사 때는 괜찮았을 수 있지만, 그 이후론 늘 실패 사례뿐이었음(동일 서비스 내에서 읽기/쓰기 경로만 분리된 경우는 예외임)

    • DB가 인터페이스라서 별도 설계가 필요하지 않다는 주장에 동의하지 않음. 여러 클라이언트가 같은 DB를 쓰면, 접근 패턴이 다르고 마이그레이션 문제도 커짐. 결국 뷰·권한관리 등 별도 추가 설계가 필요하고 유지부담도 늘어남. 이상적인 상황에선 API가 훨씬 깔끔함. 현실은 빠른 기능출시 압박 때문에 편법으로 DB 직접접근을 허용하게 되지만, 근본은 많은 이들이 새로운 요구나 디자인에 맞춰 전체 재설계를 꺼리기 때문임

    • 변경이 필요할 때, 관련된 조정 범위를 최소화하는 게 목표임. 데이터스토어 구조를 바꿔야 할 때, 해당 데이터스토어에 접근하는 모든 부분을 통제해야 하니까, 접근 경로가 적을수록 변경이 쉬워짐. 예를 들어, 실제 업무 현장에서 DB를 분리했더니 40팀 이상이 코드 고쳐야 했음. 이런 변화가 “피처 요구” 때문에 발생했다면 이 정도다. 만약 “확장성” 이슈였으면, 제품 자체가 망가질 수도 있음

    • 여러 서비스를 한 DB에 붙이는 구조를 “코드 스멜”이라고 했지만, 반대로 각각의 서비스에 물리적으로 별도 DB를 줘야 하면 가용성이 N에서 N의 M승으로 커져 실제로는 더 불안정해질 수도 있음(DB 클러스터 단위에서 얘기한다면)

  • 데이터베이스를 질의할 때는, 정말 DB에 질의하는 게 가장 효율적임. 여러 테이블의 데이터가 필요하면, 애플리케이션에서 각각 질의해서 합치는 것보다 조인을 써야 함. 그리고 뷰나 심지어 저장 프로시저도 적극 추천함. 뷰는 데이터 추상화 계층이므로 설계에 큰 도움이 되며, SQL 코드도 잘 짜면 이해하기 쉽고 유지보수도 편함

    • 이 점 때문에 ORMs이 많은 문제를 일으킴. SSR 환경의 MVC 뷰마다 SQL 뷰/커스텀쿼리를 직접 쓰는 게 대형 웹 서비스를 효율적이고 우아하게 만드는 방법임. RDBMS에게 무거운 작업을 맡기고, 웹서버는 SQL 결과를 테이블에 바로 넘겨주면 됨. MSSQL, Oracle 같은 레거시 RDBMS엔 내장 최적화가 아주 많기 때문임. 반면 ORMs은 단일 오브젝트 모델을 강요해서 유연성이 없다시피 함

    • 저장 프로시저가 유용해 보이지만, 실제론 언어(T-SQL 등) 한계로 인해 팀원들이 모두 친숙한 최신 언어로 통일해 개발하기 어려움. 대형 T-SQL 코드베이스를 유지보수하는 중인데, 버전 관리나 진단툴도 별로고, 신규 입사자 코드는 그래도 읽을 만하지만 T-SQL은 악몽임

    • 나는 동의하지 않음. 최신 확장성 위주 아키텍처에선 조인을 DB 앞단 백엔드에서 하는 것이 나음. DB에 단순 인덱스 검색을 맡기고, 조인은 백엔드에서 하도록 구조화하면 DB 확장성도 좋고 속도도 빨라짐. 서버 인스턴스 증설이 DB 증설보다 쉽기 때문임. 조인이 반드시 DB에서만 할 수 있는 어마어마한 데이터일 경우, 그때 구조를 변경해야 함. 프론트로 조인까지 옮길 수 있다면 결과 캐시도 유리하고 이득임

    • 정말 그런가? 예를 들어 고객 1만명, 주문 100만 건에서 고객 20필드+주문 5필드 테이블을 모두 조인해서 전송하면 2500만 필드를 전송하게 됨. 두 개 쿼리로 독립적으로 가져와서 조인하면, 주문 500만 필드+고객 20만 필드가 됨. 대역폭, 성능에선 이게 훨씬 나음

    • 이 규칙은 첫 출발로 괜찮지만, 언제 예외가 필요한지 잘 알아야 함. 내가 맡았던 앱은 조인 때문에 레코드가 기하급수적으로 불어나는 구조였음. 그래서 쿼리를 분리했더니 오히려 네트워크 오버헤드보다 결과 가공/필터링 이득이 커서 훨씬 빨라졌음. 나중엔 모든 데이터를 JSONB로 저장하는 구조로 바꿔서 오히려 더 나아졌음

  • 좋은 시스템 설계에 대해 얘기하면서 정작 문제 도메인은 전혀 언급하지 않은 점이 아쉽다는 생각임. 시스템 설계에서 가장 핵심이자 어려운 것은 시스템이 사용자에게 제공하는 인터페이스임. 결국 소프트웨어 시스템은 “이 기능을 제공해줄 테니, 그 대신 이런 구조/모델을 이해해야 한다”처럼 문제의 교환임. 인터페이스 설계 실수가 가장 큰 비용이고, 모든 시간의 대부분을 인터페이스에 대해 논의하지 않는다면 정말 중요한 것을 놓치고 있음. 그 밖의 시스템 요소는 나중에 사용자를 건드리지 않고 얼마든지 고칠 수 있음

  • “좋은 설계는 자기를 드러내지 않고, 나쁜 설계가 오히려 더 그럴듯해 보인다”라는 말에 현장감 있게 공감함. 기술자 평가가 “복잡함” 기준으로 이루어지면서 과설계를 장려하는 구조가 되어버린 듯함. KISS 원리가 오랫동안 충분히 인식되지 않고 있음

    • 때때로 코드베이스에서 딱히 생각하지 않고 그냥 넘어가는 부분들을 돌아보는데, 이런 게 오히려 좋은 설계를 한 흔적임

    • 이건 불행하게도 사실임. 대다수는 복잡한 솔루션에 더 매력을 느끼고, 단순한 답을 내놓으면 무능해 보인다는 인상도 있음. 하지만 현실적으로 관리가 쉬운 단순한 구조가 전체 프로젝트 성공에 더 크게 기여함. 물론 필연적으로 복잡한 문제도 있지만, 대부분은 그냥 평범한 웹앱임

  • 스키마 설계에서 가장 중요한 것은 유연성임. 데이터가 쌓이면 스키마 변경이 매우 어렵기 때문임. 하지만 너무 유연하게 설계하면(모든 데이터를 JSON에 넣거나 EAV 구조로!) 애플리케이션 코드가 한없이 복잡해지고 이상한 퍼포먼스 문제가 추가됨. 그래서 보통은 테이블 구조만 봐도 무슨 용도인지 직관적으로 알 수 있을 만큼, 사람 읽기 쉬운 스키마를 선호함. EAV, JSON 컬럼/테이블을 자주 보면 정말 개발을 그만두고 싶어짐. 분명 EAV에도 쓸모 있는 케이스가 존재하지만, 대부분의 경우엔 현장에 혼란만 줌. N+1 문제, 쿼리문 동적생성, 오디트 데이터를 동일 DB에 저장해서 그게 비즈니스 로직으로 흡수되는 패턴, 복잡한 오라클 환경, 그리고 뭘 DB에 넣을지-앱에 둘지 잘못 분리하는 설계 등, 이런 변수 하나하나가 개발자 삶의 질을 엄청나게 깎아먹음

    • 이와 관련해 Bill Karwin의 “SQL Antipatterns”라는 책에서 EAV 패턴의 위험과 한계를 잘 소개함. 그럼에도 때때로 스키마를 그리기 어려울 때(Postgres에서는 JSONB 컬럼 등) 임시 방편으로 사용할 수 있지만, 모범 규칙은 될 수 없음. 정규화가 가능하면 항상 정규화를 택하는 게 좋음

    • “오디트 데이터를 같은 DB에 저장하면 그게 결국 비즈니스 로직 일부가 돼서 곤란하다”라는 데, 그럼 “정석”은 뭔지 궁금함. 별도 DB? 완전 독립 스토리지?

  • “5개의 서비스가 동일 테이블에 쓰는 걸 피하라, 4개는 API 호출이나 이벤트만 던지고 1개만 직접 DB에 쓰라”는 조언에 대해, 최선은 애초에 5개 서비스가 동일 테이블에 쓸 일이 없게 하는 구조임. 만약 쓴다면, 실제로 서비스 간 로직이 많이 겹쳐져 있을 수도 있음. 그럼 이 5개 서비스가 과연 정말 모두 달라야 하는지, 하나로 합칠 수 없는지 고민해봐야 함. 실질적으론 별도 데이터 테이블을 주거나 리팩터링으로 오히려 문제를 해결할 수 있음

  • 상태(stateful)/비상태(stateless) 구분은 인프라와 개발 책임을 나누는 핵심임. 컨테이너로 Stateless하게 돌릴 땐 잘못될 게 많지 않으므로, 실패하면 그냥 다시 배포하면 됨. 데이터셋을 깨뜨릴 수준의 DB 실수만 피한다면 대부분 빠르게 복구 가능함. 커리어 경험, 시간, 성실도가 다양한 인력이 여기까진 괜찮음. 반면 데이터베이스, 파일스토리지 등 State가 붙는 영역은 전혀 다름. 실수 하나로 사업 전체가 위험해질 수도 있으므로, 실무 경험 풍부한 전담 인력이 맡아야 함. DB는 문제없이 작동해도 백업이 없다면 이미 큰 리스크임. 실제로 이런 영역은 몇 분 만에 배포해도 해결이 안 되는 문제임

    • “stateless 컨테이너 앱이면 큰 사고가 없다→배포하면 복구”에서, 언제부턴가 stateful 얘기로 바뀐 것 같아 논리 흐름이 이해 안 감
  • “bool 대신 timestamp로 표시” 조언에 대해, 너무 포괄적 지침 아닌지 싶음. 예를 들어 is_on → true, on_at → 1023030은 명확하지만, is_a_bear → true, a_bear_at → 12312231231은 뜬금없음. 대부분 곰은 언제 곰이 되는 게 아니니까… 특정 상황에만 맞는 얘기임

    • 나는 거의 모든 경우에 boolean 대신 timestamp나 integer를 쓰는 게 나음. 특히 상태가 둘뿐인 필드는 대개 “유형 분류”로 발전하는 경우가 많음. 예를 들어 곰만 있다고 해도 enum 타입으로 확장 가능성 대비하는 게 낫고, 상태 필드도 단순 활성/비활성 외에 다양한 상태(중지, 삭제, 일시정지 등)로 확장되기 때문에 boolean이 늘어나면서 오히려 복잡해짐. integer가 나음

    • 명제 그대로라면, DB에 boolean 쓰는 것 자체가 냄새라는 건데, 이에 동의함. 다만 이런 접근(bool → timestamp 전환 자체)이 조인에선 편의로 되는 경우가 많지, 소위 “완결적 해법”은 아님. 실시간 변경이 중요하면 애초에 오디트 테이블이 맞음. soft delete도 마찬가지로 미적지근한 해법 같다 생각함. 진짜 의도는 지우기 막기 위함인데, 사실 백업-복구로 더 효과적인 보호 가능함

    • boolean 타입이 더 작은 데이터 크기를 가지므로 일부 워크로드(분석용 대량 데이터 등)에선 효율적임. 때때로 논리적으로 boolean을 저장하는 게 맞는 경우도 있음. 예를 들어, 프로세스 결과(성공/실패 표기)는 boolean이 실용적임

    • 굳이 boolean만 timestamp로 쓸 이유가 있을지 의문임. isDarkTheme, paginationItems 등도 변경 시점이 궁금할 수 있음. 사실상 poor-man changelog라는 느낌임

    • 그런 경우라면 Bear 같은 enum 값을 쓰는 게 나음

  • 좋은 시스템 설계에 좀 더 추상적인 관점에서 배울 수 있는 책을 찾는다면, John Gall의 Systemantics 강력 추천함. 엔지니어로서 꼭 읽을 가치를 느낌

    • 이 책 짧지만 읽는 재미가 컸음. 문체가 아주 색다른 것도 인상적임