diff --git a/2026/Street_Coder/geunju-lee/Chapter_7_to_9.md b/2026/Street_Coder/geunju-lee/Chapter_7_to_9.md new file mode 100644 index 00000000..01872e2f --- /dev/null +++ b/2026/Street_Coder/geunju-lee/Chapter_7_to_9.md @@ -0,0 +1,420 @@ +## 7 ~ 9장 +--- + + +## chapter 7 - 자기주장이 뚜렷한 최적화 + +> 최적화의 초점은 “어디를 깎을 것인가”가 아니라, “어디에 시간을 쓸 것인가”다. + +이 챕터를 읽으면서 느낀 점은, +책이 말하는 최적화의 방향이 다소 **지엽적인 레벨에 치우쳐 있다**는 것이다. + +물론 바이트 단위 최적화나 메모리 레이아웃 같은 주제는 +특정 환경에서는 중요한 문제일 수 있다. +하지만 현재 내가 일하는 환경, 즉 + +- JVM 기반 (Kotlin / Java) +- 클라우드 환경 (Kubernetes) +- 분산 시스템 + +에서는 **최적화의 우선순위 자체가 다르다**고 느꼈다. + +--- + +### 내가 생각하는 현실적인 최적화 우선순위 + +요즘 시스템에서는 +CPU나 메모리가 부족하면 **스케일 아웃으로 해결하는 선택지**가 항상 존재한다. + +물론 무한정 확장할 수 있는 것은 아니지만, +적어도 초기 단계에서 + +> “이 코드를 5% 더 빠르게 만들 수 있을까?” + +를 고민하는 것보다 + +> “이 구조가 병목을 만들고 있지는 않은가?” + +를 고민하는 것이 훨씬 더 큰 효과를 만든다. + +내 기준에서 중요한 최적화는 다음과 같다. + +- DB 인덱싱 (쿼리 성능) +- 동기 vs 비동기 구조 선택 +- 불필요한 네트워크 호출 제거 +- 캐싱 전략 + +이 요소들은 단순한 성능 개선이 아니라, +**시스템 전체의 처리량과 지연 시간에 직접적인 영향을 준다.** + +--- + +### Byte 레벨 최적화에 대한 거리감 + +책에서는 바이트 단위의 최적화 이야기도 나오지만, +이 부분은 솔직히 실무에서 체감이 크지 않았다. + +나는 Kotlin / Java를 사용하고 있고, +대부분의 경우 + +- `List` +- `String` +- 객체 기반 모델 + +을 사용한다. + +이 환경에서는 메모리 구조나 바이트 배열을 직접 다루기보다는, +**JVM이 추상화한 레벨 위에서 개발하는 것이 일반적**이다. + +물론 극단적인 성능이 필요한 영역이라면 +이런 최적화가 의미를 가질 수 있겠지만, + +- 일반적인 백엔드 서비스 +- 특히 비즈니스 로직 중심의 시스템 + +에서는 **우선순위가 낮은 문제**라고 느꼈다. + +--- + +### 그렇다면 이 챕터가 의미 없는가? + +그렇지는 않다고 생각한다. + +이 챕터의 핵심은 +“어떤 최적화를 해야 하는가”가 아니라, + +**“최적화는 선택이며, 그 선택에는 명확한 의도가 있어야 한다”**는 점이다. + +다만 그 선택의 대상이 + +- 바이트 단위가 될 수도 있고 +- 아키텍처 구조가 될 수도 있다 + +는 점에서, +나는 이 챕터를 **좀 더 상위 레벨의 최적화 관점으로 재해석**해서 받아들였다. + +--- + +### 정리 + +- 모든 최적화는 비용 대비 효과로 판단해야 한다 +- 현대 백엔드 환경에서는 구조적인 최적화가 훨씬 중요하다 +- 저수준 최적화는 특정 상황에서만 의미를 가진다 + +결국 중요한 건 +**“지금 시스템에서 가장 큰 병목이 어디인가”를 정확히 찾는 것**이다. + +--- +## chapter 8 - 기분 좋은 확장성 + +> 확장성은 기술 선택의 문제가 아니라, “어디까지 책임을 나눌 것인가”에 대한 문제다. + +이 챕터를 읽으면서 몇 가지 지점에서 의문이 들었다. +특히 thread, NOLOCK, 그리고 모놀리스에 대한 관점은 +현재 내가 사용하는 환경과는 다소 결이 다르게 느껴졌다. + +--- + +### Thread에 대한 생각 + +책에서는 thread 사용에 대해 꽤 보수적인 입장을 취한다. +하지만 내 기준에서는 + +> thread 자체가 문제라기보다, **thread safe 하지 않은 상태가 문제**라고 생각한다. + +즉, + +- 공유 자원 접근 +- race condition +- deadlock + +이런 문제를 인지하고 관리할 수 있다면 +thread 사용 자체를 피할 이유는 없다고 본다. + +--- + +### Coroutine을 쓰면서 느낀 점 + +나는 Kotlin coroutine을 사용하다가 +실제로 문제가 된 경험이 있다. + +- 비동기 흐름이 복잡해지면서 추적이 어려워짐 +- context 전파 문제 +- 예상하지 못한 동시성 이슈 + +이 경험을 떠올려보면, +책에서 말하는 “thread를 조심하라”는 메시지는 + +> 단순히 thread를 쓰지 말라는 것이 아니라, +> **동시성 모델 자체의 복잡성을 경계하라는 의미**에 더 가깝다고 느꼈다. + +즉, + +- thread +- coroutine +- async/await + +어떤 모델을 쓰든 +**추상화 뒤에 숨겨진 동시성 문제는 사라지지 않는다**는 점이 핵심이다. + +--- + +### NOLOCK에 대한 낯섦 + +책에서 언급되는 NOLOCK은 +솔직히 실무에서 거의 사용해본 적이 없다. + +(주로 MySQL / JPA 환경에서는 직접적으로 다루지 않는 영역이기도 하다) + +이 부분은 환경 차이도 크다고 느꼈다. + +- 특정 DB (예: SQL Server)에서는 중요한 옵션일 수 있지만 +- 내가 사용하는 환경에서는 크게 고려하지 않는 요소였다 + +그래서 이 부분은 +“확장성의 핵심 요소”라기보다는 +**특정 기술 스택에 종속된 이야기**로 받아들였다. + +--- + +### “Thread를 사용하지 말라”는 말의 해석 + +책의 문장을 그대로 받아들이면 +“thread를 쓰지 말라”는 극단적인 결론처럼 보인다. + +하지만 실제 의미는 + +> “동시성으로 문제를 해결하려다가, +> 더 큰 복잡성을 만들지 말라” + +에 가깝다고 생각한다. + +즉, + +- 성능을 위해 무조건 병렬화하지 말고 +- 구조적으로 해결할 수 있는 문제는 구조로 풀어라 + +라는 메시지로 이해했다. + +--- + +### 모놀리스를 존중하라는 부분에 대한 생각 + +이 챕터에서 가장 공감이 덜 됐던 부분은 +“모놀리스를 존중하라”는 메시지였다. + +직관적으로 보면 + +> MSA가 더 확장성에 유리하지 않나? + +라는 생각이 들기 때문이다. + +하지만 이 부분은 단순히 “옛날 책이라서”라기보다는, +다음과 같이 해석하는 게 더 맞다고 느꼈다. + +--- + +### 모놀리스 vs MSA는 성능 문제가 아니다 + +MSA가 확장성에 유리한 것은 맞다. +하지만 그 전제는 + +- 서비스 간 완전한 분리 +- 네트워크 비용 감당 +- 운영 복잡성 관리 + +가 가능할 때다. + +반대로 모놀리식은 + +- 네트워크 비용 없음 +- 트랜잭션 단순 +- 디버깅 용이 + +라는 강점이 있다. + +즉, + +> 작은 규모에서는 모놀리식이 오히려 더 “효율적으로 확장”될 수 있다. + +이 관점에서 보면 +책의 메시지는 충분히 납득이 된다. + +--- + +### 내가 내린 결론 + +- thread 자체는 문제가 아니다 → **동시성 복잡성이 문제다** +- coroutine도 결국 같은 문제를 가진다 +- 특정 DB 옵션(NOLOCK)은 환경에 따라 중요도가 다르다 +- 모놀리스 vs MSA는 “확장성 우열” 문제가 아니다 + +결국 이 챕터는 + +> “확장성을 위해 무조건 복잡한 선택을 하지 말라” + +는 메시지를 전달하고 있다고 느꼈다. + +--- + +### 정리 + +확장성은 단순히 기술을 바꾼다고 얻어지는 것이 아니다. + +- 구조를 단순하게 유지할 것인가 +- 복잡성을 감수하고 분리할 것인가 + +이 선택의 문제다. + +그리고 그 선택은 +**현재 시스템의 규모, 팀, 운영 역량에 따라 달라져야 한다.** + +--- + +## chapter 9 - 버그와 동거 + +> 버그 대응의 핵심은 “잡는다”가 아니라, “흐르게 한다”에 가깝다. + +이 챕터는 전체적으로 공감되는 방향이 많았지만, +구체적인 방법론에서는 다소 다른 생각이 들었다. + +특히 try-catch 사용 방식과 로깅에 대한 부분이다. + +--- + +### try-catch에 대한 생각 + +책에서는 try-catch를 통해 +버그를 흡수하고 안정적으로 처리하는 방향을 이야기한다. + +하지만 내 기준에서는 + +> 모든 에러를 catch해서 처리하는 것이 항상 좋은 방법은 아니다. + +오히려 + +- 에러를 불필요하게 삼켜버리거나 +- 의미 없는 fallback 값을 리턴하게 되는 경우 + +문제를 더 늦게 발견하게 만들 수 있다고 생각한다. + +그래서 나는 보통 다음과 같은 방식을 선호한다. + +- 에러는 가능한 한 **상위로 전파** +- 필요한 경우에만 **의미 있는 단위에서 catch** +- 최종적으로는 **응답 레벨에서 포맷만 맞춰 반환** + +즉, + +- 비즈니스 로직에서는 에러를 숨기지 않고 +- API 레이어에서만 일관된 응답 형태로 변환한다 + +이 방식이 +문제의 원인을 더 명확하게 드러내고, +디버깅에도 더 유리하다고 느꼈다. + +--- + +### 에러 래핑 전략 + +에러를 그대로 던지기보다는 +의미 있는 단위로 래핑하는 것은 중요하다고 생각한다. + +예를 들어 + +- 외부 API 에러 → ExternalServiceException +- DB 에러 → PersistenceException + +이처럼 컨텍스트를 담아 재정의하면 + +- 로그 가독성 향상 +- 에러 분류 가능 +- 대응 전략 분리 + +가 가능해진다. + +결국 중요한 건 + +> 에러를 숨기는 것이 아니라, **의미를 추가하는 것**이다. + +--- + +### “print로 로깅하라”에 대한 생각 + +책에서는 디버깅 대신 print 기반 로깅을 권장하는 흐름이 있다. +이 방향 자체는 이해는 된다. + +- 재현이 어려운 환경에서 로그는 유일한 단서가 될 수 있다 +- 특히 운영 환경에서는 디버깅 자체가 불가능하다 + +하지만 문제는 + +> 로그는 많을수록 좋은 것이 아니라, **의미 있을수록 좋은 것**이라는 점이다. + +무분별하게 로그를 남기면 + +- 로그 양 폭증 +- 비용 증가 (스토리지 / 수집 / 분석) +- 신호 대비 노이즈 증가 + +결과적으로 +정작 중요한 로그를 찾기 어려워진다. + +--- + +### 내가 생각하는 로깅 기준 + +그래서 나는 로그를 남길 때 +다음 기준을 중요하게 생각한다. + +- 에러 로그: 반드시 남긴다 (stack trace 포함) +- 비즈니스 이벤트: 필요한 최소한만 남긴다 +- 디버그 로그: 필요할 때만 활성화 + +그리고 가능하면 + +- requestId / traceId 기반 추적 +- 구조화된 로그(JSON) + +를 통해 +**적은 로그로도 흐름을 복원할 수 있도록 만드는 것**이 더 중요하다고 본다. + +--- + +### 디버깅 vs 로깅 + +책에서는 디버깅보다 로깅을 강조하지만, +내 생각은 조금 다르다. + +- 개발 단계 → 디버깅이 훨씬 빠르고 정확하다 +- 운영 단계 → 로깅이 필수다 + +즉, + +> 둘 중 하나를 선택하는 문제가 아니라, +> **상황에 따라 다르게 써야 하는 도구**라고 생각한다. + +--- + +### 내가 내린 결론 + +- try-catch는 “무조건 잡는다”가 아니라 **의미 있는 위치에서만 사용** +- 에러는 숨기지 말고 **위로 전파 + 래핑** +- 로깅은 많게가 아니라 **정확하게** +- 디버깅과 로깅은 대체 관계가 아니라 **보완 관계** + +--- + +### 정리 + +버그와 동거한다는 것은 +버그를 무조건 흡수하는 것이 아니라, + +- 드러내고 +- 추적 가능하게 만들고 +- 복구 가능하게 만드는 것 + +이라고 생각한다. + +결국 안정성은 +**예외를 얼마나 잘 “관리”하느냐에 달려 있다.**