반응형
반응형

[System] Everything I know about good system design, 좋은 시스템 설계

 


  • 좋은 시스템 설계
    란 복잡해 보이지 않고 오랜 기간 별다른 문제가 발생하지 않는 형태
  • 상태(state) 를 다루는 것이 시스템 설계에서 가장 어려운 부분이며, 가능한 한 상태를 저장하는 컴포넌트 수를 줄이는 방향이 중요
  • 데이터베이스는 주로 상태가 보관되는 위치로, 스키마 설계와 인덱싱, 병목 해소에 중점을 둔 접근이 필요
  • 캐싱, 이벤트 처리, 백그라운드 작업 등은 성능 및 유지보수를 위해 신중하게 도입해야 하며, 남용을 피하는 것이 좋음
  • 복잡한 설계보다는 충분히 검증된 간단한 컴포넌트 및 방법론을 적절하게 사용하는 것이 지속 가능하고 안정적인 시스템 구축에 핵심

시스템 설계의 정의와 전체적인 접근

  • 소프트웨어 설계가 코드의 조립이라면, 시스템 설계는 다양한 서비스를 조합하는 과정
  • 시스템 설계의 주요 구성 요소는 앱 서버, 데이터베이스, 캐시, 큐, 이벤트 버스, 프록시 
  • 좋은 설계는 "특별한 문제가 없다", "생각보다 쉽게 끝나다", "이 부분은 신경 쓸 필요가 없다"와 같은 반응을 이끌어냄
  • 반대로 복잡하고 눈에 띄는 설계는 근본 문제를 감추거나, 과도한 설계를 나타낼 수 있음
  • 복잡한 시스템은 초기부터 바로 도입하기보다는, 최소한의 작동 가능한 단순한 구조에서 점차 발전하는 방향이 유리함

상태(state)와 무상태(stateless)의 구분

  • 소프트웨어 설계에서 가장 까다로운 부분이 바로 상태 관리
  • 정보를 저장하지 않고 즉시 결과를 반환하는 서비스(GitHub의 PDF 렌더링과 같은)는 무상태적임
  • 반면 데이터베이스에 쓰기를 수행하는 서비스는 상태를 관리함
  • 시스템 내 상태 저장 컴포넌트를 최대한 줄이는 것이 좋음. 이는 시스템의 복잡도와 장애 발생 가능성을 낮춤
  • 상태 관리를 한 서비스에서만 수행하고, 나머지 서비스는 API 호출 혹은 이벤트 발생 등 무상태 역할에 집중하는 구조가 권장됨

데이터베이스 설계와 병목 지점

스키마 및 인덱스 설계

  • 데이터 보관을 위해서는 사람이 읽기 쉬운 형태의 스키마 설계가 필요함
  • 너무 유연한 스키마(예: 전체를 JSON 컬럼에 저장)는 애플리케이션 코드 및 성능에 부담을 줄 수 있음
  • 쿼리가 빈번하게 발생할 컬럼을 기준으로 적절한 인덱스를 설정해야 함. 모든 것에 인덱스를 거는 것은 오히려 쓸데없는 오버헤드 발생

병목 해결 방법

  • 데이터베이스 접근이 종종 무거운 병목이 됨
  • 가능한 한 복잡한 데이터를 애플리케이션에서가 아닌 데이터베이스 내에서 조인(JOIN) 등으로 처리하는 것이 성능상 유리함
  • ORM 사용 시, 루프 안에서 쿼리를 발생시키는 실수를 주의해야 함
  • 필요에 따라 쿼리를 분할하여, 데이터베이스의 부담이나 쿼리 복잡도를 조절하는 것도 한 방법
  • 읽기 쿼리는 복제본(read-replica)으로 분산하여, 주(Write) 노드의 부하를 줄이는 전략이 효과적
  • 대량의 쿼리가 몰릴 때 트랜잭션 및 쓰기 연산은 데이터베이스를 쉽게 과부하 상태로 만들 수 있으므로, 쿼리 스로틀링(제한) 처리를 고려해야 함

느린 작업과 빠른 작업의 분리

  • 사용자가 인터랙션하는 작업은 수백 밀리초 내 응답이 필요
  • 시간이 오래 걸리는 작업(예: 대용량 PDF 변환 등)은 최소한의 작업만 프론트에서 즉시 제공하고, 나머지는 백그라운드로 넘기는 패턴이 효과적
  • 백그라운드 작업은 일반적으로 큐(예: Redis)와 잡 러너가 묶여 동작
  • 멀리 예약된 작업 처리는 Redis보다 DB 테이블을 별도로 만들어 관리하고, 스케줄러를 이용하여 실행하는 형태가 실용적

캐싱

  • 캐싱은 동일하거나 비싼 연산을 반복하는 경우 비용 절감과 성능 향상에 기여
  • 보통 캐시를 처음 배운 주니어 엔지니어는 모든 것을 캐시하고 싶어하고, 경험 많은 엔지니어일수록 캐시 도입은 신중
  • 캐시는 새로운 상태를 도입하므로, 동기화 이슈/오류/스테일 데이터 등의 위험이 존재
  • 먼저 쿼리의 인덱스 추가 같은 성능 개선을 시도한 뒤 캐싱을 적용하는 것이 바람직
  • 대용량 캐시는 Redis/Memcached가 아니라 S3/Azure Blob Storage와 같은 문서 저장소에 주기적으로 저장하는 방식도 활용 가능

이벤트 처리

  • 대부분의 기업은 이벤트 허브(예: Kafka) 를 갖추고, 다양한 서비스가 이벤트 기반으로 분산 처리
  • 이벤트의 남발보다는, 단순한 요청–응답 API 설계가 로깅과 문제 해결 면에서 더 유용
  • 이벤트 기반 처리는 발신자가 수신자 동작에 신경 쓰지 않아도 될 때, 혹은 고용량·지연 허용 시나리오에 적합

데이터의 전달 방식: 푸시와 풀

  • 데이터 전달에는 Pull(요청 후 응답)  Push(변경 시 자동 전달) 두 방식이 있음
  • Pull 방식은 단순하지만 반복 요청/과부하 문제가 발생
  • Push 방식은 서버에서 데이터 변경 시 클라이언트에 즉시 전달하므로, 효율적이며 최신 데이터 유지에 유리
  • 대량 클라이언트 처리에는 각각 방식에 맞게 인프라(이벤트 큐, 여러 캐시 서버 등) 확장이 필요

핫패스(Hot Paths) 집중

  • 핫패스란 시스템 내에서 가장 중요하고 데이터가 많이 흐르는 경로를 의미
  • 핫패스는 선택지가 적고, 설계 실패 시 서비스 전체에 심각한 문제를 유발할 수 있으므로, 신중한 설계가 필수
  • 옵션이 다양한 마이너 기능보다, 핫패스에 집중하여 설계 및 테스트에 자원을 배분하는 것이 효과적

로깅, 메트릭, 추적

  • 장애 발생 시 원인 진단을 위해, 비정상 경로(unhappy path)에 대한 상세 로그 기록을 적극적으로 해야 함
  • 시스템 자원(CPU/메모리), 큐 크기, 요청/작업 시간 등 기본적인 관측성 지표 수집이 필요
  • 평균값만 보는 대신 p95, p99 지연 시간 같은 분포 지표도 반드시 관찰해야 함. 상위 소수의 느린 요청이 핵심 사용자의 문제일 수 있음

킬스위치, 재시도, 장애 복구

  • 킬스위치(시스템 일시 차단) , 재시도의 전략적 활용이 중요함
  • 무작정 재시도는 다른 서비스에 부담만 주며, 사전에 회로 차단기(circuit breaker) 등으로 요청을 제어해야 효과적임
  • Idempotency Key(멱등키) 도입으로 동일 요청 재처리 시 중복 작업 방지가 가능함
  • 일부 장애 상황에서 열린 실패(fail open) 또는 닫힌 실패(fail closed) 중 선택이 필요함. 예를 들어, Rate Limiting은 fail open(허용) 쪽이 사용자 영향이 적은 방향임. 반면 인증은 fail closed가 필수임

마무리

  • 서비스 분리, 컨테이너, VM 도입, 트레이싱 등 일부 주제는 생략됐으나, 잘 검증된 컴포넌트를 적재적소에 사용하는 것이 장기적으로 가장 안정적인 시스템 구축으로 이어짐
  • 기술적으로 특별한 설계는 실제로 매우 드물며, 지루할 정도로 단순한 설계가 오히려 실무에서 가장 자주 쓰임
  • 본질적으로 좋은 시스템 설계란 눈에 띄지 않고, 충분히 입증된 방법론을 안전하게 조합하는 과정임

 

https://news.hada.io/topic?id=22580

 

좋은 시스템 설계 | GeekNews

좋은 시스템 설계란 복잡해 보이지 않고 오랜 기간 별다른 문제가 발생하지 않는 형태상태(state) 를 다루는 것이 시스템 설계에서 가장 어려운 부분이며, 가능한 한 상태를 저장하는 컴포넌트 수

news.hada.io

 

반응형
반응형

[SQL] 여러 개의 UPDATE 문을 실행한 후, 최종적으로 영향을 받은 전체 행의 개수를 세고 싶다

 

@@ROWCOUNT는 직전에 실행된 SQL 문이 영향을 준 행의 개수를 반환하는 전역 변수예요. 여러 개의 UPDATE 문을 실행할 경우, 각 UPDATE 문 직후에 이 값을 변수에 누적해서 더하면 최종 결과를 얻을 수 있어요.

 

-- 총 업데이트된 행 수를 저장할 변수 선언
DECLARE @TotalRowCount INT = 0;

-- 첫 번째 UPDATE 문 실행
UPDATE dbo.Customers
SET CustomerStatus = 'VIP'
WHERE TotalPurchases >= 100000;

-- 첫 번째 UPDATE의 영향을 받은 행 수를 변수에 더하기
SET @TotalRowCount = @TotalRowCount + @@ROWCOUNT;

-- 두 번째 UPDATE 문 실행
UPDATE dbo.Customers
SET CustomerStatus = 'Gold'
WHERE TotalPurchases >= 50000 AND TotalPurchases < 100000;

-- 두 번째 UPDATE의 영향을 받은 행 수를 변수에 더하기
SET @TotalRowCount = @TotalRowCount + @@ROWCOUNT;

-- 최종적으로 업데이트된 총 행 수 출력
PRINT '총 ' + CAST(@TotalRowCount AS NVARCHAR(10)) + '개의 행이 업데이트되었습니다.';
반응형
반응형

개발자들이 조심해야 할 함정들

https://qouteall.fun/qouteall-blog/2025/Traps%20to%20Developers#summarization-of-traps

 

Traps to Developers | qouteall notes

A summarization of some traps to developers. There traps are unintuitive things that are easily misunderstood and cause bugs.

qouteall.fun

  • 개발자들이 흔히 빠지는 직관적이지 않은 함정을 정리하여, 발생하기 쉬운 버그의 원인을 소개함
  • HTML, CSS, Unicode/텍스트 인코딩, 부동소수점, 시간 등 다양한 기술에서 자주 발생하는 문제점들을 다룸
  • 각각의 언어와 프레임워크에서 문법 및 동작의 미묘한 차이로 인해 오해나 오류가 생길 수 있음을 강조
  • 동시성, 네트워킹, 데이터베이스 등 백엔드 핵심 영역에서 실제 운영 환경에서 발생할 수 있는 함정들을 예시로 설명함
  • 다양한 예제와 참고 링크를 통해 문제 상황과 해결법, 그리고 예기치 않은 동작 개선점을 안내함

 

HTML과 CSS

  • Flexbox/Grid에서 min-width 기본값
    • min-width는 기본적으로 auto
    • min-width: auto는 콘텐츠 크기에 의해 결정되며, flex-shrink, overflow: hidden, width: 0, max-width: 100%보다 우선 적용
    • 권장: min-width: 0 명시
  • CSS에서 가로와 세로의 차이
    • width: auto는 부모 공간 채우기를 시도, height: auto는 콘텐츠에 맞춤
    • inline, inline-block, float 요소의 width: auto는 확장하지 않음
    • margin: 0 auto는 가로 중앙 정렬, margin: auto 0은 세로 중앙 정렬 불가 (단, flex-direction: column에서는 세로 중앙 가능)
    • 마진 병합은 세로에서만 발생
    • writing-mode: vertical-rl 등 레이아웃 방향이 바뀌면 동작도 반전
  • Block Formatting Context (BFC)
    • display: flow-root로 BFC 생성 (그 외 overflow: hidden/auto/scroll, display: table 등도 가능하나 부작용 존재)
    • 세로 인접 형제 마진이 겹치거나, 자식 마진이 부모 밖으로 새는 현상은 BFC로 방지 가능
    • 부모가 float 자식만 포함하면 높이가 0으로 무너짐 → BFC로 수정 가능
    • border나 padding이 있으면 마진 병합 발생하지 않음
  • Stacking Context
    • 새로운 stacking context를 만드는 조건
      • transform, filter, perspective, mask, opacity 등 렌더링 속성
      • position: fixed 또는 sticky
      • z-index 지정 + absolute/relative 위치 지정
      • z-index 지정 + flexbox/grid 내부 요소
      • isolation: isolate
    • 특징
      • z-index는 stacking context 내부에서만 적용
      • position: absolute/fixed 좌표는 가장 가까운 positioned 조상 기준
      • sticky는 stacking context를 넘어서 동작하지 않음
      • overflow: visible도 stacking context에 의해 잘림
      • background-attachment: fixed는 stacking context 기준으로 배치
  • 뷰포트 단위
    • 모바일 브라우저에서 주소창/네비게이션바 스크롤 시 화면에서 사라지면 100vh 값이 달라짐
    • 최신 해결책: 100dvh 사용
  • Absolute Position 기준
    • position: absolute는 부모가 아닌, 가장 가까운 relative/absolute 또는 stacking context 조상을 기준
  • Blur 동작
    • backdrop-filter: blur는 주변 요소를 고려하지 않음
  • Float 무효화
    • 부모가 flex 또는 grid면 자식의 float는 효과 없음
  • 퍼센트 단위 width/height
    • 부모의 크기가 사전에 결정되지 않으면 작동하지 않음 (순환 참조 회피 목적)
  • Inline 요소 특성
    • display: inline은 width, height, margin-top, margin-bottom 무시
  • Whitespace 처리
    • 기본적으로 HTML의 줄바꿈은 공백으로 취급, 연속 공백은 하나로 축소
    • <pre>는 공백 축소 방지하지만 시작/끝 부분 동작 특이
    • 대부분 콘텐츠 시작/끝 공백은 무시되나 <a>는 예외
    • inline-block 사이의 공백/줄바꿈은 실제 간격으로 표시됨 (flex/grid에서는 발생 안 함)
  • text-align
    • 텍스트 및 inline 요소 정렬에는 적용되지만 block 요소 정렬에는 적용되지 않음
  • box-sizing
    • 기본값은 content-box → padding/border 포함 안 됨
    • width: 100% + padding 설정 시 부모 영역 넘침 가능
    • 해결: box-sizing: border-box
  • Cumulative Layout Shift
    • <img>에 width와 height 속성을 지정하지 않으면 이미지 로딩 지연으로 레이아웃 흔들림 발생
    • 권장: 속성 지정으로 CLS 방지
  • Chrome에서 파일 다운로드 네트워크 요청
    • DevTools 네트워크 패널에 표시되지 않음 (다른 탭으로 처리됨)
    • 분석 필요 시 chrome://net-export/ 사용
  • HTML 내 JavaScript 파싱 문제
    • <script>console.log('</script>')</script> 같은 경우 첫 </script>를 종료 태그로 인식
    • 참고: Safe JSON in script tags

Unicode와 텍스트 인코딩

  • 코드 포인트와 그래프림 클러스터
    • 그래프림 클러스터는 GUI에서의 "문자 단위"
    • 가시적 ASCII 문자는 코드 포인트 1개 = 그래프림 클러스터 1개
    • 이모지는 여러 코드 포인트로 구성된 하나의 그래프림 클러스터일 수 있음
    • UTF-8에서 코드 포인트는 1~4바이트, 바이트 수와 코드 포인트 수는 일치하지 않음
    • UTF-16에서 코드 포인트는 2바이트 또는 4바이트(서로게이트 페어)
    • 표준은 클러스터 내 코드 포인트 수 제한을 두지 않으나, 구현에서는 성능상 제한 존재
  • 언어별 문자열 동작 차이
    • Rust: 내부 문자열 UTF-8 사용, len()은 바이트 수, 직접 인덱싱 불가, chars().count()는 코드 포인트 수, UTF-8 유효성 엄격 검증
    • Golang: 문자열은 사실상 바이트 배열, 길이와 인덱싱은 바이트 단위, 주로 UTF-8 사용
    • Java, C#, JS: UTF-16 기반, 2바이트 단위로 길이 측정, 인덱싱도 2바이트 단위, 서로게이트 페어 존재
    • Python: len()은 코드 포인트 수 반환, 인덱싱은 코드 포인트 하나를 포함하는 문자열 반환
    • C++: std::string은 인코딩 제약 없음, 바이트 벡터처럼 동작, 길이/인덱싱은 바이트 단위
    • 언급된 언어 중 그래프림 클러스터 단위로 길이/인덱싱하는 언어는 없음
  • BOM (Byte Order Mark)
    • 일부 텍스트 파일은 BOM을 가짐, 예: EF BB BF → UTF-8 인코딩 표시
    • 주로 Windows에서 사용, 비-Windows 소프트웨어는 BOM 처리 못할 수 있음
  • 기타 주의사항
    • 바이너리 데이터를 문자열로 변환 시, 잘못된 부분은 � (U+FFFD)로 대체됨
    • Confusable characters 존재 (서로 비슷해 보이는 문자)
    • 정규화(Normalization): 예) é는 U+00E9(단일 코드 포인트) 또는 U+0065+U+0301(두 코드 포인트)로 표현 가능
    • Zero-width characters  Invisible characters 존재
    • 줄바꿈 차이: Windows는 CRLF \r\n, Linux/MacOS는 LF \n
    • 한자 통합(Han unification): 언어별로 모양이 조금 다른 문자가 동일 코드 포인트 사용
      • 폰트가 언어별 변형을 포함하여 적절히 렌더링
      • 국제화 시 올바른 폰트 변형 선택 필요

부동소수점 (Floating point)

  • NaN 특성
    • NaN은 자기 자신을 포함한 어떤 값과도 같지 않음 (NaN == NaN은 항상 false)
    • NaN != NaN은 항상 true
    • NaN을 포함한 연산 결과는 대부분 NaN으로 전파됨
  • 특수한 값
    • +Inf와 -Inf 존재, NaN과는 다름
    • -0.0은 +0.0과 구분되는 값
      • 비교 연산에서는 동일하지만, 일부 계산에서는 다르게 동작
      • 예: 1.0 / +0.0 == +Inf, 1.0 / -0.0 == -Inf
  • JSON과의 호환성
    • JSON 표준은 NaN과 Inf를 허용하지 않음
      • JS JSON.stringify는 NaN, Inf를 null로 변환
      • Python json.dumps(...)는 NaN, Infinity를 그대로 출력 (표준 위반)
        • allow_nan=False 옵션 시 NaN/Inf가 있으면 ValueError 발생
      • Golang json.Marshal은 NaN/Inf 존재 시 에러 반환
  • 정밀도 문제
    • 부동소수점 직접 비교는 실패 가능 → abs(a - b) < ε 형태 권장
    • JS는 모든 숫자를 부동소수점으로 처리
      • 안전한 정수 범위는 -(2^53 - 1) ~ 2^53 - 1
      • 이 범위를 벗어나면 정수 표현이 부정확
      • 큰 정수에는 BigInt 사용 권장
      • JSON에 안전 범위를 넘어선 정수가 포함되면 JSON.parse 결과 값은 부정확할 수 있음
      • 밀리초 단위 타임스탬프는 287,396년까지 안전, 나노초 단위는 문제 발생
  • 연산 법칙 비적용
    • 연산 순서에 따라 정밀도 손실로 인해 결합법칙, 분배법칙이 엄밀히 성립하지 않음
    • 병렬 연산(행렬 곱셈, 합계 등)은 비결정적 결과를 만들 수 있음
  • 성능
    • 나눗셈은 곱셈보다 훨씬 느림
    • 동일한 수로 여러 번 나눌 때는 역수를 먼저 구해 곱하는 방식으로 최적화 가능
  • 하드웨어에 따른 차이
    • FMA(Fused Multiply-Add) 지원 여부: 일부 하드웨어는 더 높은 정밀도로 중간 계산
    • Subnormal range 처리: 최신 하드웨어는 지원하지만 일부 구형은 0으로 처리
    • 반올림 모드 차이
      • RNTE(가장 가까운 짝수로 반올림), RTZ(0으로 절단) 등 존재
      • x86/ARM은 스레드 로컬 mutable 상태로 설정 가능
      • GPU는 명령어 단위로 반올림 모드가 다름
    • 삼각함수, 로그 등 수학 함수 동작 차이
    • x86은 레거시 80비트 FPU와 per-core rounding mode 존재 → 사용 비권장
    • 이 외에도 다양한 요인으로 하드웨어별 부동소수점 결과가 달라질 수 있음
  • 정밀도 향상 방법
    • 계산 그래프를 얕게 구성 (곱셈 연속 구조 줄이기)
    • 중간 값이 매우 크거나 매우 작은 경우 피하기
    • FMA 같은 하드웨어 연산 활용

시간 (Time)

  • 윤초(Leap second)
    • Unix 타임스탬프는 윤초를 무시
    • 윤초 발생 시 주변 구간에서 시간이 늘어나거나 줄어듦(Leap smear)
  • 시간대(Time zone)
    • UTC 및 Unix 타임스탬프는 전 세계 공통
    • 사람이 읽는 시간은 지역별 시간대에 의존
    • DB에는 타임스탬프를 저장하고 UI에서 변환하는 방식 권장
  • 서머타임(DST)
    • 일부 지역에서는 여름철에 1시간 시계 조정
  • NTP 동기화
    • 동기화 과정에서 시간이 "뒤로 가는" 상황 발생 가능
  • 서버 시간대 설정
    • 서버는 UTC로 설정 권장
    • 분산 시스템에서 노드별 시간대가 다르면 문제 발생
    • 시스템 시간대 변경 후 DB 재설정 또는 재시작 필요
  • 하드웨어 시계 vs 시스템 시계
    • 하드웨어 시계는 시간대 개념 없음
    • Linux: 하드웨어 시계를 UTC로 처리
    • Windows: 하드웨어 시계를 로컬 시간으로 처리

Java

  • ==는 객체 참조 비교, 객체 내용 비교는 .equals 사용 필요
  • equals와 hashcode를 오버라이드하지 않으면 map/set에서 객체 동일성을 참조 기반으로 판단
  • map의 key 객체나 set 원소 객체의 내용을 변경하면 컨테이너 동작이 깨짐
  • List<T> 반환 메서드는 경우에 따라 mutable ArrayList 또는 immutable Collections.emptyList() 반환, 후자 수정 시 UnsupportedOperationException 발생
  • Optional<T>를 반환하는 메서드가 null을 리턴하는 경우 존재 (권장되지 않음)
  • finally 블록에서 return 시, try 또는 catch에서 발생한 예외가 무시되고 finally 반환 값이 적용됨
  • interrupt 무시하는 라이브러리 존재, IO 포함한 클래스 초기화 과정이 interrupt로 깨질 수 있음
  • thread pool에서 .submit()으로 전달한 task의 예외는 기본적으로 로그에 출력되지 않고 future로만 확인 가능, future 무시하면 예외 확인 불가
    • scheduleAtFixedRate 작업은 예외 발생 시 조용히 중단됨
  • 숫자 리터럴이 0으로 시작하면 8진수 처리 (0123 → 83)
  • 디버거는 지역 변수의 .toString()을 호출, 일부 클래스의 toString()에 부작용이 있어 디버깅 시 코드 동작이 달라질 수 있음 (IDE에서 비활성화 가능)

Golang

  • append()는 capacity 여유 시 메모리 재사용, subslice에 append 시 부모 메모리까지 덮어쓸 수 있음
  • defer는 함수 리턴 시 실행, 블록 스코프 종료 시가 아님
  • defer는 mutable 변수 캡처
  • nil 관련
    • nil slice와 empty slice는 다름
    • string은 nil 불가, 빈 문자열만 존재
    • nil map은 읽기는 가능하지만 쓰기는 불가
    • interface nil 특이 동작: data pointer가 null이지만 type info가 null이 아니면 nil과 같지 않음
  • Dead wait: Go에서 실제 동시성 버그 사례 존재
  • Timeout 종류 다양, net/http에서 상세히 다룸

C/C++

  • std::vector 원소 포인터 저장 후 vector가 grow되면 재할당 발생, 포인터 무효화
  • 리터럴 문자열로 생성된 std::string은 임시 객체일 수 있음, c_str() 호출 시 위험
  • 반복 중 컨테이너 수정 시 iterator 무효화 발생
  • std::remove는 실제 삭제가 아닌 원소 재배열, 삭제는 erase 필요
  • 숫자 리터럴이 0으로 시작하면 8진수 처리 (0123 → 83)
  • Undefined behavior (UB): 최적화 과정에서 UB는 자유롭게 바뀔 수 있어 의존 시 위험
    • 초기화되지 않은 메모리 접근 UB
    • char*를 struct 포인터로 변환 시 객체 수명 시작 전 접근으로 UB, memcpy로 초기화 권장
    • 잘못된 메모리 접근 (null 포인터 등) UB
    • 정수 overflow/underflow UB (unsigned는 0 아래로 underflow 가능)
    • Aliasing: 서로 다른 타입 포인터가 동일 메모리 참조 시 strict aliasing rule에 의해 UB 발생
      • 예외: 1) 상속관계 타입 2) char*, unsigned char*, std::byte* 변환 (역변환은 적용 안 됨)
      • 강제 변환은 memcpy 또는 std::bit_cast 권장
    • Unaligned memory 접근 UB
  • 메모리 Alignment
    • 64비트 정수는 주소가 8로 나누어 떨어져야 함
    • ARM에서 unaligned 접근은 crash 가능
    • 바이트 버퍼를 struct로 직접 해석 시 alignment 문제 발생
    • alignment는 struct padding을 만들어 메모리 낭비 가능
    • 일부 SIMD 명령어(AVX 등)는 정렬된 데이터만 처리 가능, 보통 32바이트 alignment 필요

Python

  • 함수 기본 인자는 호출 시마다 새로 생성되지 않고 최초 값이 그대로 저장됨

SQL Databases

  • Null 처리
    • x = null은 동작하지 않고 x is null을 사용해야 함
    • Null은 자기 자신과 같지 않음 (NaN과 유사)
    • Unique index는 Null 중복 허용 (단, Microsoft SQL Server는 예외)
    • select distinct에서 Null 처리 방식은 DB마다 다름
    • count(x)와 count(distinct x)는 Null 값이 있는 행을 무시
  • 일반 동작
    • 날짜 암묵 변환은 timezone 의존적일 수 있음
    • 복잡한 join + distinct는 중첩 쿼리보다 느릴 수 있음
    • MySQL(InnoDB)에서 string 필드가 utf8mb4가 아니면 4-byte UTF-8 문자 삽입 시 오류 발생
    • MySQL(InnoDB)은 기본적으로 대소문자 구분 없음
    • MySQL(InnoDB)은 암묵적 변환 허용: select '123abc' + 1; → 124
    • MySQL(InnoDB) gap lock은 deadlock 유발 가능
    • MySQL(InnoDB)에서는 group by와 select 컬럼 불일치 시 비결정적 결과 반환
    • SQLite에서는 strict가 아니면 필드 타입이 크게 의미 없음
    • Foreign key는 암묵적 lock을 발생시켜 deadlock을 유발할 수 있음
    • Locking은 DB별로 repeatable read isolation을 깨뜨릴 수 있음
    • 분산 SQL DB는 locking 미지원이거나 특이한 동작 가능 (DB별 상이)
  • 성능/운영
    • N+1 query 문제는 각 쿼리가 빠르기 때문에 slow query log에 나타나지 않음
    • 장기 실행 트랜잭션은 lock 문제 등 유발 → 트랜잭션은 빠르게 끝내는 것이 권장됨
    • 전체 테이블 lock 사례
      • MySQL 8.0+에서는 unique index/foreign key 추가 시 대부분 동시 처리 가능
      • 구버전 MySQL은 전체 테이블 lock 발생 가능
      • mysqldump에 --single-transaction 옵션 없으면 전체 테이블 read lock
      • PostgreSQL에서 create unique index나 alter table ... add foreign key는 전체 테이블 read lock 유발
        • 회피: create unique index concurrently 사용
        • foreign key는 ... not valid  validate constraint 방식 사용
  • Range 쿼리
    • 겹치지 않는 범위:
      • 단순 조건 p >= start and p <= end는 비효율적 (복합 인덱스 있어도)
      • 효율적 방식:
        select *   
        from (select ... from ranges where start <= p order by start desc limit 1)   
        where end >= p  
        
        (start 컬럼 인덱스만 필요)
    • 겹칠 수 있는 범위:
      • 일반 B-tree 인덱스로는 비효율적
      • MySQL은 spatial index, PostgreSQL은 GiST 사용 권장

Concurrency and Parallelism

  • volatile
    • volatile은 lock을 대체할 수 없으며 atomicity 제공 안 함
    • lock으로 보호된 데이터는 volatile 필요 없음 (lock이 memory order 보장)
    • C/C++: volatile은 일부 최적화만 방지, memory barrier 추가 안 됨
    • Java: volatile 접근은 sequentially-consistent ordering 제공 (필요 시 JVM이 memory barrier 삽입)
    • C#: volatile 접근은 release-acquire ordering 제공 (필요 시 CLR이 memory barrier 삽입)
    • 메모리 읽기/쓰기 재정렬 관련 잘못된 최적화 방지 가능
  • TOCTOU (Time-of-check to time-of-use) 문제
  • SQL DB에서 응용 계층 제약 조건 처리
    • 단순 unique index로 표현 불가한 제약(예: 두 테이블 간 유니크, 조건부 유니크, 기간 내 유니크)을 애플리케이션에서 강제하는 경우:
      • MySQL(InnoDB): repeatable read 레벨에서 select ... for update 후 insert, 그리고 유니크 컬럼에 인덱스가 있으면 gap lock 덕분에 유효 (단, gap lock은 고부하 시 deadlock 유발 가능 → deadlock detection 및 retry 필요)
      • PostgreSQL: repeatable read 레벨에서 동일 로직은 동시성 상황에서 불충분 (write skew 문제)
        • 해결책:
          • serializable isolation level 사용
          • 애플리케이션 대신 DB 제약 사용
            • 조건부 유니크 → partial unique index
            • 두 테이블 간 유니크 → 별도 테이블에 중복 데이터 삽입 후 unique index
            • 기간 배타성 → range type + exclude constraint
  • Atomic reference counting
    • Arc, shared_ptr와 같이 많은 스레드가 동일 카운터를 자주 변경하면 성능 저하
  • Read-write lock
    • 일부 구현은 read lock에서 write lock으로 업그레이드 지원하지 않음
    • read lock 보유 상태에서 write lock 시도 시 deadlock 발생 가능

Common in many languages

  • Null/None/nil 체크 누락이 흔한 오류 원인
  • 반복문 중 컨테이너 수정 시 단일 스레드 데이터 경쟁 발생 가능
  • 가변 데이터 공유 실수: 예) Python에서 [[0] * 10] * 10은 올바른 2D 배열 생성 아님
  • (low + high) / 2는 overflow 가능 → 안전한 방식은 low + (high - low) / 2
  • 단락 평가(short circuit): a() || b()는 a가 true면 b 실행 안 됨, a() && b()는 a가 false면 b 실행 안 됨
  • 프로파일러 기본값은 CPU time만 포함 → DB 대기 등은 flamegraph에 나타나지 않아 오해 유발
  • 정규 표현식 dialect가 언어마다 다름 → JS에서 동작하는 정규식이 Java에서 동작 안 할 수 있음

Linux and bash

  • 디렉터리 이동 후 pwd는 원래 경로, 실제 경로는 pwd -P
  • cmd > file 2>&1 → stdout+stderr 모두 파일, cmd 2>&1 > file → stdout만 파일, stderr는 그대로
  • 파일 이름은 대소문자 구분 (Windows와 다름)
  • 실행 파일은 capability 시스템 존재 (getcap으로 확인)
  • Unset 변수 위험: DIR unset이면 rm -rf $DIR/  rm -rf / 실행 위험 → set -u로 방지 가능
  • 환경 적용: 스크립트를 현재 shell에 적용하려면 source script.sh 사용 → 영구 적용하려면 ~/.bashrc에 추가
  • Bash는 명령어 캐싱: $PATH 내 파일 이동 시 ENOENT 발생 → hash -r로 캐시 갱신
  • 변수 미인용 사용 시 줄바꿈이 공백으로 처리
  • set -e: 스크립트 오류 시 즉시 종료하지만, 조건문 내부(||, &&, if)에서는 동작 안 함
  • K8s livenessProbe와 디버거 충돌: 브레이크포인트 디버거는 앱 전체를 멈추게 하여 health check 응답 실패 → Pod가 종료될 수 있음

React

  • 렌더링 코드에서 state 직접 수정
  • Hook을 if/loop 안에서 사용 → 규칙 위반
  • useEffect dependency array에 필요한 값 누락
  • useEffect에서 정리(clean up) 코드 누락
  • Closure trap: 오래된 state 캡처로 인해 버그 발생
  • 잘못된 위치에서 데이터 변경 → 불순한 컴포넌트
  • useCallback 사용 누락 → 불필요한 리렌더링 발생
  • 메모된 컴포넌트에 비메모 값 전달 시 memo 최적화 무효화

Git

  • Rebase는 히스토리 재작성
    • rebase 후 일반 push는 충돌 → 반드시 force push 필요
    • remote branch 히스토리 변경 시 pull도 --rebase 사용
    • --force-with-lease는 일부 경우 다른 개발자 commit 덮어쓰기 방지 가능, 단 fetch만 하고 pull 안 하면 보호 안 됨
  • Merge revert 문제
    • Merge revert는 효과 불완전 → 동일 브랜치 다시 merge 시 아무 변화 없음
    • 해결책: revert의 revert 실행 또는 깨끗한 방법(backup → reset → cherry-pick → force push)
  • GitHub 관련 주의사항
    • API 키 같은 secret을 commit 후 force push로 덮어도 GitHub에는 기록이 남음
    • private repo A를 fork한 B가 private이라도, A가 public이 되면 B의 내용도 공개됨 (삭제 후에도 접근 가능)
  • git stash pop: conflict 발생 시 stash가 drop되지 않음
  • .DS_Store는 macOS가 자동 생성 → .gitignore에 **/.DS_Store 추가 권장

Networking

  • 일부 라우터·방화벽은 유휴 TCP 연결을 조용히 끊음 → HTTP 클라이언트·DB 클라이언트의 커넥션 풀 무효화 가능 → 해결: TCP keepalive 설정
  • traceroute 결과는 신뢰성 낮음 → 경우에 따라 tcptraceroute가 더 유용
  • TCP slow start는 대기시간 증가 원인 → tcp_slow_start_after_idle 비활성화로 해결 가능
  • TCP sticky packet 문제: Nagle 알고리즘은 패킷 전송 지연 → TCP_NODELAY 활성화로 해결 가능
  • Nginx 뒤에 백엔드 배치 시 커넥션 재사용 설정 필요 → 미설정 시 고부하 환경에서 내부 포트 부족으로 연결 실패
  • Nginx는 기본적으로 패킷 버퍼링  SSE(EventSource) 지연 발생
  • HTTP 표준은 GET·DELETE 요청 body를 금지하지 않음 → 일부는 body 사용하지만 많은 라이브러리·서버가 지원하지 않음
  • 하나의 IP에 여러 웹사이트 호스팅 가능 → 구분은 HTTP Host 헤더와 TLS의 SNI가 담당 → 단순 IP 접속 불가 사이트 존재
  • CORS: 다른 origin 요청 시 브라우저는 응답 접근 차단 → 서버에서 Access-Control-Allow-Origin 헤더 설정 필요
    • 쿠키 전달 포함 시 추가 설정 필요
    • 프론트엔드와 백엔드가 동일 도메인·포트라면 CORS 문제 없음

Other

  • YAML 주의사항
    • YAML은 공백 민감  key:value는 오류, key: value가 올바름
    • 국가 코드 NO는 따옴표 없이 쓰면 false로 해석되는 문제 발생
    • Git commit hash를 따옴표 없이 쓰면 숫자로 변환될 수 있음
  • Excel CSV 문제
    • Excel은 CSV 열 때 자동 변환 수행
      • 날짜 변환: 1/2, 1-2  2-Jan
      • 대형 숫자 부정확 변환: 12345678901234567890  12345678901234500000
    • 원인은 Excel이 내부적으로 floating point로 숫자를 처리하기 때문
    • 이슈로 인해 유전자 이름 SEPT1이 잘못 변경된 사례 존재

 

 

 

반응형
반응형

 

escape , encodeURIComponent 차이

 

 

escape()와 encodeURIComponent()는 모두 문자열을 인코딩하는 JavaScript 함수이지만, 사용 목적과 인코딩 방식에서 중요한 차이가 있습니다. 웹 환경에서 데이터를 안전하게 전송하기 위해 주로 encodeURIComponent()를 사용하고, escape()는 더 이상 권장되지 않습니다.

 

주요 차이점 요약표

특징 escape() encodeURIComponent()
목적 HTML 문자열 인코딩 (구식, 비권장) URL 컴포넌트 (값, 경로) 인코딩 (권장)
공백 인코딩 + 또는 %20 (브라우저마다 다름, 비일관적) 항상 %20 (표준)
한글/유니코드 %uxxxx (비표준, UTF-8과 호환 안 됨) UTF-8 기반 %xx (표준, 대부분의 웹 환경과 호환)
특수 문자 /, @, *, + 등을 인코딩하지 않음 &, =, ?, #, / 등 URL 특수 문자도 인코딩
현재 상태 더 이상 사용 권장되지 않음 표준이며 널리 사용됨
반응형
반응형

[python] pyx - The next step inPython packaging

 

https://astral.sh/pyx

 

pyx | Astral

A Python-native package registry from the creators of uv.

astral.sh

 

https://news.hada.io/topic?id=22514

 

  • pyx는 uv 개발팀이 만든 Python 네이티브 패키지 레지스트리로, PyPI·PyTorch·사설 소스 설치 속도를 최대 10배 향상
  • 기존 패키지 레지스트리 범위를 넘어, 속도·보안·GPU 인식 기능을 제공하며, 내부 패키지와 PyPI·PyTorch 같은 공개 소스 모두 지원
  • 패키지 인기, 생성 시기, 취약점 여부 등 기준으로 필터링 가능한 전용 인덱스 URL을 제공해 보안성과 컴플라이언스를 강화
  • Python에 특화된 최신 표준 지원과 uv와의 직접 통합을 통해 설정 없이 인증과 사용이 가능함
  • 팀 내 중복 빌드, PyTorch·CUDA 설치 난이도, 빌드 깨짐, 인증 불편 등 엔터프라이즈 환경의 주요 문제를 서버-클라이언트 통합으로 해결
  • GPU 인식 기능으로 하드웨어에 맞는 PyTorch, vLLM, FlashAttention, DeepSpeed 등의 사전 빌드 버전을 일관된 메타데이터와 최적 구성으로 제공함
  • 최적화된 아티팩트와 uv 네이티브 메타데이터 API를 통해 다른 사설 레지스트리 대비 월등한 성능을 제공

Astral의 비전과 배경

  • Astral은 Python 생태계를 위한 고성능 개발 도구를 만드는 회사로, Ruff(린터·포매터)와 uv(패키지 매니저)로 잘 알려짐
  • 창업 배경은 Python이 세계에서 가장 인기 있는 프로그래밍 언어임에도 불구하고 툴링 측면에서 충분히 지원받지 못하고 있음을 느꼈기 때문임
  • 현재 Astral 도구 체인은 월 1억 건 이상 설치, uv는 하루 5억 건 이상의 요청을 처리하며 폭발적으로 성장 중임
  • 목표는 Python을 가장 생산적인 프로그래밍 생태계로 만드는 것이며, 이를 위해 클라이언트 도구를 넘어 Python 클라우드를 구축하려 함

pyx 소개

  • pyx는 uv의 최적화된 백엔드로 설계된 Python 네이티브 패키지 레지스트리
    • 내부 패키지 호스팅 가능
    • PyPI, PyTorch 인덱스 같은 공개 소스에 대한 가속·설정 가능 프런트엔드 역할
  • 주요 특징
    • 빠른 설치 속도 : 패키지 설치 및 빌드 최적화
      • PyPI, PyTorch, 내부 프라이빗 소스에서 패키지 설치 시 최적화된 아티팩트와 uv 네이티브 메타데이터 API 활용
      • 타 사설 레지스트리 대비 최대 10배 빠른 속도 제공
    • 보안 및 규정 준수 강화 : 의존성·공급망 이해를 통한 위험 최소화
      • 패키지 필터링을 위한 전용 인덱스 URL 생성 가능
      • 인기, 배포 연령, 취약점 상태 등의 기준으로 패키지 접근 제어
      • 서버 측에서 재현 가능한 빌드 보장
    • 최신 표준 지원
      • Python에 특화된 최신 패키징 표준과 워크플로를 지원
      • uv와 직접 통합돼 별도 설정 없이 원활한 인증 및 사용 가능
    • GPU 인식 패키지 배포 : CUDA·PyTorch 관련 빌드 및 배포 단순화
      • PyTorch, vLLM, FlashAttention, DeepSpeed 등 GPU 관련 라이브러리의 맞춤형 사전 빌드 제공
      • 하드웨어 기반 최적 구성과 일관된 메타데이터 유지

해결하려는 문제

  • PyTorch·CUDA·FlashAttention·DeepSpeed 등 GPU 관련 라이브러리 설치의 어려움
  • 팀 내 동일 패키지의 반복 빌드로 인한 리소스 낭비
  • setuptools 업데이트로 인한 빌드 오류
  • 내부 레지스트리 인증 과정의 불편함

서버-클라이언트 통합 전략

  • uv(클라이언트)  pyx(서버) 의 수직 통합으로 위 문제들을 직접 해결
  • pyx 없이 uv만, 또는 uv 없이 pyx만 사용 가능하지만 함께 사용할 때 최고의 경험 제공
  • 오픈소스 도구와의 깊은 통합으로 기존에는 불가능했던 개발 경험 구현 가능

비즈니스 모델

  • uv, Ruff, ty 등 Astral 도구는 영원히 무료·오픈소스·퍼미시브 라이선스 유지
  • 대신 pyx와 같은 유료 호스팅 서비스를 제공해 “다음 단계” 인프라 수요 충족

 

 

반응형
반응형

Rust, Python, TypeScript: 새로운 프로그래밍 3대장(Trifecta)

 

 

  • Rust, Python, TypeScript가 앞으로 소프트웨어 개발의 중심 언어로 부상할 것으로 예상됨
  • AI 코딩 도구의 보편화로 인해 개발자의 언어 충성도가 약화되고, 실용적이고 생태계가 강한 언어가 선택받게 됨
  • 아이디어 중심 프로그래밍(idea-oriented programming) 패러다임이 등장, 개발자는 설계와 검토에 집중하고 구현은 AI에게 맡기는 구조로 변화함
  • 이 세 언어는 고급 타입 시스템 강력한 패키지 매니저 생태계를 모두 갖추고 있어, AI 활용과 협업, 오류 방지에 유리함
  • 에러 메시지 품질, 생태계, 플랫폼 연계성 등도 점점 더 중요한 기준이 되고 있음

Rust, Python, TypeScript가 떠오르는 이유

  • Rust는 시스템 소프트웨어, 고성능이 필요한 영역, 타입 안전성과 메모리 효율성이 탁월함
  • Python은 실험·프로토타이핑·수학/과학 라이브러리 활용에 최적, 빠르게 아이디어를 검증 가능
  • TypeScript는 웹, 브라우저, 다양한 플랫폼에 바로 적용 가능하며, 강력한 타입 시스템과 npm 생태계로 확장성 확보

AI가 바꾸는 개발 패러다임: 아이디어 중심 프로그래밍

  • AI 코딩 도구(Large Language Model) 보편화로 프로그래밍이 '아이디어 구상→AI가 구현→사람이 설계/검토'로 변화 중
  • Vibe coding은 즉흥적·모호한 명령에 가깝지만, Idea-oriented programming은 설계와 구조, 지속 가능한 원칙에 집중
  • 개발자는 주요 설계/방향 제시, AI는 반복적/구현적 작업 담당, 아키텍트와 견습생의 관계에 비유

언어 선택 기준의 변화

  • 예전에는 자신이 잘 아는 언어(예: Rust)로 빠르게 작업했으나, AI와 함께 일할 땐 필요한 라이브러리, 성능, 플랫폼 연계성이 더 중요해짐
  • 프로젝트에 따라 Python(머신러닝), TypeScript(웹·VSCode Extension), Rust(기본 선택) 등 상황별 최적 언어를 선택

타입 시스템의 중요성

  • AI 코딩 보조 환경에서는 고급 타입 시스템이 더 중요해짐
  • Rust와 TypeScript는 상태와 데이터 유효성, 실수 방지를 코드 레벨에서 강제, AI가 실수할 확률도 줄어듦
  • Python 역시 mypy, pydantic 등 설정을 통해 타입 안정성을 확보 가능

생태계와 패키지 매니저의 가치 상승

  • AI와 함께 개발할 때는 대형 라이브러리 활용이 쉬워져서, 생태계의 크기와 패키지 매니저의 품질이 더욱 중요해짐
  • Rust(cargo), TypeScript(npm), Python(uv 등) 모두 현대적인 패키지 매니저를 갖춤

사소한 문법과 워크어라운드는 덜 중요해지지만, 에러 메시지와 가이드 품질은 여전히 중요

  • LLM(AI)은 반복적 문법 실수는 빠르게 고치지만, 에러 메시지/가이드가 명확해야 효과적으로 보정 가능
  • Rust 커뮤니티의 언어 친화성(ergonomics) 노력처럼, AI도 좋은 에러 메시지를 활용해 더 나은 코드를 생산

결론: LLM이 강력한 개발 도구를 모두에게 열어줌

  • 아이디어 중심 프로그래밍 시대에는 주니어도 Principal Engineer급 설계/지휘 경험 가능
  • 코딩 자체의 ‘플로우’가 줄어든다는 우려도 있으나, 설계와 아이디어 중심의 개발 경험이 더 가치 있어질 전망

► AI와 함께하는 시대, Rust, Python, TypeScript가 강력한 타입, 생태계, 플랫폼 지원으로 개발 현장의 표준으로 자리 잡고 있음

반응형

+ Recent posts