웹 개발자의 KPI(Key Performance Indicator)는 개인의 업무 성과와 팀 목표를 측정하는 데 도움이 됩니다.
아래는 일반적인 주니어 웹 개발자를 위한 KPI 항목입니다:

  1. 코드 품질 및 생산성
    • 코드 리뷰 피드백 반영률: 코드 리뷰에서 지적된 사항을 얼마나 잘 반영했는지.
    • 주간/월간 코드 기여도: 작성한 코드의 라인 수보다는 기능의 완성도와 기여도를 평가.
    • 버그 발생률: 작성한 코드에서 발견된 버그의 수.
    • 테스트 커버리지 향상: 테스트 작성 여부와 코드 커버리지 증가율.

  2. 프로젝트 완료 및 기여
    • 기한 준수율: 맡은 작업이나 프로젝트를 제때 완료한 비율.
    • 작업 완료 건수: 주어진 업무(예: Jira 티켓, Git 이슈) 처리 수량.
    • 기능 구현의 정확성: 요구사항을 충족하며 기능을 개발한 정도.

  3. 학습 및 성장
    • 새로운 기술 습득: 업무와 관련된 새로운 기술, 프레임워크, 툴을 학습한 정도.
    • 교육 참여: 팀 내 세미나, 워크숍 또는 외부 교육 프로그램 참여율.
    • 지식 공유: 팀원들에게 학습한 내용을 공유하거나, 문서화한 빈도.

  4. 협업 및 커뮤니케이션
    • 커뮤니케이션 명확성: 팀원 및 이해관계자와 명확히 소통하는 능력.
    • 팀워크 기여도: 코드 리뷰 참여, 동료와의 협업 빈도 및 품질.
    • 이슈 해결 참여: 문제를 발견하고 해결하는 데 기여한 빈도.

  5. 운영 및 유지보수
    • 배포 안정성: 개발한 코드가 운영 환경에서 문제 없이 배포된 비율.
    • 긴급 문제 해결 기여: 운영 환경에서 발생한 문제를 얼마나 효과적으로 지원했는지.
    • 기존 코드 개선: 리팩토링, 성능 최적화 등 기존 코드에 대한 기여.

  6. 사용자 중심 개발
    • 사용자 피드백 반영률: 사용자 요구사항을 코드에 잘 반영했는지.
    • UX/UI 향상 기여도: 사용자 경험과 인터페이스 개선 기여.

KPI 예시

항목 목표 설정 예시
코드 리뷰 반영률 코드 리뷰 피드백 90% 이상 반영
작업 완료 건수 주간 티켓 5개 이상 완료
테스트 커버리지 신규 기능 테스트 커버리지 80% 이상
신규 기술 학습 분기별 1개 기술 블로그 작성
커뮤니케이션 명확성 회의 후 작업 명확도 95% 이상

주니어 개발자 KPI 설정 팁

  1. 구체적이고 측정 가능하게: 모호한 목표 대신 측정 가능한 목표로 설정.
  2. 성과와 성장의 균형: 단순히 업무량뿐 아니라, 개인의 성장과 학습도 포함.
  3. 주기적인 리뷰: 분기별 또는 월간으로 KPI를 점검하여 조정.
  4. 팀과 회사 목표 연계: 개인 목표가 팀 및 회사 목표와 맞물리도록 설정.

😊

728x90
반응형

1. 테스트 프로세스

테스트 프로세스는 테스트와 관련된 활동이 체계적으로 진행되어 의도된 테스트 목적과 목표를 달성할 수 있도록 모든 구성요소를 엮어주는 역할을 함.

테스트 프로세스 5 단계

  계획/제어 -> 분석/설계 -> 구현/실행 -> 완료/리포팅 -> 마감

테스트 프로세스는 계획/제어, 분석/설계, 구현/실행, 완료/리포팅, 마감 5단계로 구성.

  1. 계획과 제어 :
    테스트 계획 수립은 테스트 목표와 임무를 달성하기 위해 이를 확인하고 필요한 활동을 정의.
    테스트 제어는 계획 대비 실제 신행 상황을 비교하는 지속적인 활동.
  2. 분석과 설계 :
    테스트 분석과 설계는 일반적이고 추상적인 테스트 목적을 실제적이고 구체적인 테스트 상황과 테스트 케이스로 변환.
  3. 구현과 실행 :
    테스트 구현과 실행은 가장 효율적이고 효과적으로 테스트스를 실행하기 위하여 테스트 케이스를 조합하고 테스트 실행에 필요한 다른 정보를 포함하는 테스트 프로시져를 명세화.
  4. 완료 조건과 리포팅 :
    초기에 정의된 테스트 목표에 비해 어느 정도 실제 테스트가 수행되었는지를 평가.
  5. 테스트 마감 :
    완료된 테스트에서 발견된 사실 및 수집된 데이터, 경험을 취합하고 축적.

2. TC 작성 절차

  문서 수집 -> TC 작성 -> 내부 검토 -> 커버리지 분석 -> 승인
  1. 참조 문서 수집 :
    테스트 계획서에 명시된 테스트 케이스 작성 지침과 수준을 고려,
    테스트 설계에 필요한 분석 및 설계 문서 수집.
  2. TC 작성 :
    테스트 설계 기법을 이용하여 TC를 작성.
  3. 내부 검토 :
    PM, 아키텍트, 디자이너, 기획자, 개발자, QA 담당자가 작성된 TC의 적정성 검토.
  4. 요구사항 대비 커버리지 분석 :
    TC가 어느 정도 요구사항을 반영하는가에 대한 분석.
    기본적으로 테스트 가능한 요구사항은 모두 TC에 반영되어있는지 확인.
  5. 승인 :
    작성된 TC를 클라이언트(현업), 기획자 및 PM에게 승인을 받음.

3. TC 구성요소

  • 식별 번호 :
    TC의 고유 식별자.
  • 이슈 번호, 제목 :
    제목과 이슈 번호를 기입. (Redmine NO., Jira NO.)
  • 요약 (Description) :
    TC의 목표 등 요약된 정보
  • 사전 조건 (Precondition) :
    테스트 수행에 필요한 조건 및 실행환경. (선행 조건, 전제조건)
  • 종속성 (Dependencies) :
    테스트 요구사항 또는 기타 TC에 대한 의존성 판단.
  • 수행 절차 (Test Step) :
    TC를 수행하기 위한 정확한 단계.
  • 기대 결과 (Expect Result) :
    절차대로 진행 시 테스트 통과 여부를 결정하는 기대 결과.
  • 실제 결과 (Actual Result, PASS / FAIL) :
    테스트 수행 후 실제 결과.
  • 비고 (Remark, Note, Comment) :
    기타 비고 사항을 기입.
  • 그 외 :
    우선순위, 모듈 이름, 테스트 설계자, 설계일, 테스트 수행자, 수행일 등

구성 요소는 조직이나 프로젝트에 따라 변경/추가/삭제 될 수 있다.


4. TC 작성 시 주의 점 및 장/단점

주의점

  • 절차의 누락 :
    테스트 절차를 정확하게 입력하지 않을 경우 테스트 수행의 어려움이 생기는 테이스 발생 가능.
  • 장황한 설명 :
    TC 작성 시 상세하고 충분한 정보를 제공해야 하지만, 너무 많은 단어와 불필요한 설명으로 소통의 오류 유발 가능.
  • 전문 용어의 과다 사용 :
    TC 작성 시, 직군간 소통이 불가능한 전문용어를 과다사용시 테스트 수행에 어려움이 있을 수 있음.
  • 분명하지 않은 PASS/FAIL 기준 :
    테스트 수행 후 테스트 결과가 통과인지 실패인지 예상 결과에 정확히 기입하지 않아 결과 판단에 어려움 유발 가능.

단점

  • TC 작성 시간이 수행 시간보다 오래 걸릴 수 있음.
  • 기능 변화에 따른 TC 변경 :
    기능을 자주 변경한다면 추후 테스트 케이스의 통제에 어려움이 생기게 될 수 있음.
  • 배경 지식 판단의 어려움 :
    TC를 작성하는 사람은 테스트 하는 기능을 잘 알고 있으나, 테스트 수행자는 배경 지식이 없을 경우 테스트 수행 시간이 길어지거나 수행에 어려움이 있을 수 있음.

장점

  • 이력 참조 :
    TC는 어플리케이션 런칭 후에도 사용, 유지보수 팀과 추후 어플리케이션의 버전을 담당자의 테스트 이력 참고가 가능.
  • 테스트 진행 상황 추적 :
    TC를 문서화 하면 수행한 TC의 수, 통과/실패 수, 과업 범위 별 케이스 수, 테스트 커버리지 등의 정보를 추적/확인 가능.
  • 반복성 :
    잘 작성된 TC는 누구나 반복적으로 수행 가능.

5. TC 정렬

TC의 정렬은 업부의 효율과 연결될 수 있다. 테스트의 흐름, 테스트 환경 등을 고려해서 정렬 하도록 하자.

흐름이 이어지는 TC 정렬

정렬 전 스펙 문서에 따라 쓴 TC 정렬 후 흐름에 맞춘 TC
1. A 기능 1 실행 1. A 기능 1 실행
2. A 기능 2 실행 2. A 기능 1의 추가기능 실행
3. A 기능 3 실행 3. 기능 2 실행
4. A 기능 1의 추가기능 실행 4. A 기능 2의 추가기능 실행
5. A 기능 2의 추가기능 실행 5. 기능 3 실행
6. A 기능 3의 추가기능 실행 6. A 기능 3의 추가기능 실행

환경이 비슷한 TC 정렬

정렬 전 스펙 문서에 따라 쓴 TC 정렬 후 흐름에 맞춘 TC
1. 기능 A를 관리자(Root) 계정으로 테스트 1. 기능 A를 관리자(Root) 계정으로 테스트
2. 기능 A를 사용자(User) 계정으로 테스트 2. 기능 B를 관리자(Root) 계정으로 테스트
3. 기능 B를 관리자(Root) 계정으로 테스트 3. 기능 C를 관리자(Root) 계정으로 테스트
4. 기능 B를 사용자(User) 계정으로 테스트 4. 기능 A를 사용자(User) 계정으로 테스트
5. 기능 C를 관리자(Root) 계정으로 테스트 5. 기능 B를 사용자(User) 계정으로 테스트
6. 기능 C를 사용자(User) 계정으로 테스트 6. 기능 C를 사용자(User) 계정으로 테스트


TC-template-20240510.xlsx
0.02MB

728x90
반응형
 

아이젠하워 매트릭스

  • 업무의 우선순위를 먼저 해야 할 일, 계획해야 할 일, 위임할 일, 하지 않아도 될 일 4단계로 분류

업무 우선순위

  1. 자신의 업무를 모두 나열
  2. 각 업무의 긴급성과 중요도 판단
  3. 업무를 4개의 카테고리로 분류
    1. 긴급하고 중요한 일 - 먼저 해야할 일
    2. 긴급하지 않지만 중요한 일 - 계획해야 할 일
    3. 긴급하지 않지만, 중요하지 않은 일 - 위임할 일
    4. 긴급하지도, 중요하지 않은 일 - 하지 않아도 될 일
  4. 아이젠하워 매트릭스에 따라 우선순위 확인하고, 일정 계획

아이젠하워 매트릭스

 

아이젠하워 매트릭스

  1. 중요하고 급한 일 - 먼저 한다
    명확한 결과가 따르며 장기 목표에도 영향을 주는 업무로서, 가장 먼저 해야 하는 업무입니다.
    이 카테고리 안의 업무들은 미루지 않고 바로 처리하는 것이 좋습니다.
  2. 중요하지만 급하지 않은 일 - 계획한다
    장기 목표에 영향을 미치지만 당장 처리하지 않아도 되는 일입니다.
    긴급하지 않기 때문에 신중하게 준비하고 계획하여 완성도를 높일 수 있습니다.
    중요하고 급한 일을 해결한 뒤 이 카테고리 안의 업무를 처리합니다.
    이 업무들을 마냥 미뤄두지는 않도록 주의해야 하며, 집중력을 발휘하여 수행할 수 있도록 합니다.
  3. 중요하지 않지만 급한 일 - 위임한다
    바로 처리되어야 하지만 장기 목표에는 영향을 미치지 않는 업무입니다.
    이러한 업무는 에너지 소모가 많지만 특별한 기술은 요하지 않는 잔업인 경우가 많습니다.
    꼭 본인이 직접 하지 않아도 되는 일이기 때문에, 다른 사람에게 위임하거나 대체 시스템을 마련하도록 합니다.
  4. 중요하지도 급하지도 않은 일 - 하지 않는다
    목표를 달성하는 데 방해가 되는 일입니다.
    업무 목록에서 삭제하도록 합니다.

긴급한 일과 중요한 일을 구별하는 법

  • 긴급한 일
    • 즉시 해야 하는 일로, 기한 안에 처리하지 못하면 명확한 결과가 나타납니다.
      • 예) 기한이 임박한일, 장애, 긴급 지시사항, CS 이슈 등
  • 중요한 일
    • 당장 하지 않아도 되지만, 장기적인 목표에 도움이 되는 일입니다.
      • 예) 신 기능 스터디, 프로젝트 계획, 3rd Party 솔루션 도입

참고자료

https://1891ghaon.tistory.com/117

 


업무의 우선순위 7단계

  1. 모든 업무가 포함된 하나의 작업 목록을 만들기
    1. 효과적으로 우선순위 정하는 첫 단계는 업무의 전체 범위를 파악하는 것부터 시작합니다.
    2. 모든 업무가 기록되면 각 업무의 중요성, 긴급성, 소요 시간, 보상을 적습니다.
  2. 중요한 사항 식별하기 : 진정한 목표 이해하기
    1. 우선순위를 정하는 일은 즉각적인 '시간 관리 전략'처럼 보일 수 있지만 장기적인 '목표 달성 전략'이기도 합니다.
    2. 설정된 목표(큰 그림)를 위해 자신이 어떤 일을 하고 있는지 이해해야 미래 결과와 가장 관련이 있는 업무파악할 수 있습니다.
    3. 최종 목표에 영향을 주지 않는 작업으로 하루를 채우는 것은 시간 낭비입니다.
      항상 목표를 염두해 두고 있어야 합니다.
  3. 긴급한 사항을 강조 표시하기
    • 할 일 목록에서 마감일이 눈에 띄도록 하면 신속하게 완료해야 하는 작업을 알 수 있습니다.
    • 공식적으로 필요하지 않은 경우에도 마감일을 설정해야 합니다. 그렇지 않으면 중요한 작업을 계속 미루게 될 것입니다.
  4. 중요성 긴급성을 바탕으로 우선순위를 정한다.(아이젠하워 매트릭스)
    • 중요하고 긴급한 일 : 먼저 수행합니다.
    • 중요하지만 긴급하지 않은 일 : 전략적 계획을 세우고 완료일을 정합니다.
    • 긴급하지만 중요하지 않은 일 : 일을 축소하거나 권한을 위임합니다.
    • 긴급하지도 중요하지도 않은 일 : 할 일 목록에서 삭제합니다.
  5. 우선순위가 겹치는 것은 피한다
  6. 한 번에 하나의 중요한 작업에 집중해야 합니다.
    여러 가지의 우선 순위를 계속 시도하고 관리하는 것은 성과 저하로 이어집니다.
  7. 시간과 노력을 고려한다
  8. 할 일 목록이 너무 많아 부담스럽다면 최소한의 시간과 노력이 드는 작업에 우선순위를 두고 신속하게 진행하세요.
    작업을 정리하면 여유와 함께 작업을 추진할 수 있는 성취감이 생깁니다.
  9. 끊임없이 검토하고 현실을 반영해라
  10. 업무 목록과 우선순위자주 검토하는 것이 '통제력 및 집중력 유지'에 중요합니다.

 

참고자료

 

 
728x90
반응형

캐시 설계 전략

  Caching은 H/W, S/W 전반에 걸쳐 사용되는 기술이다.
  DB - application 뿐 아니라 클라이언트나 CDN에서도 캐싱을 사용한다.
  이 글에서는 DB - Web Application의 캐싱만 다룬다.

서비스 응답시간에 큰 영향을 미치는 부분은 주로 네트워크 통신과 DB I/O이다. 캐싱을 통하면 DB I/O로 인해 발생하는 오버헤드를 줄일 수 있다.

서론

Caching?

캐시는 빠른 응답과 비용 절약을 위해 사용하는 임시 데이터다.

  • 클라이언트의 요청으로 특정 데이터가 필요한 경우 서버는 DB에 질의한 뒤 결과를 반환한다.
  • 만약 요청하는 데이터의 캐시가 존재한다면 DB에 질의하지 않고 캐시로부터 데이터가 반환된다.
  • 캐기사 없다면? DB에 질의가 수행되어 반횐되고 그 결과를 캐싱한다.

캐시의 관리

캐시는 임시 데이터다. 따라서 캐시를 저장할 때 캐시가 만료되는 시간 (expire time || TTL; Time To Live)를 명시한다.
캐시는 해당 시간 내에서만 유효하고 만료 시간이 경과하면 사용할 수 없다.

캐시에 만료되는 시간을 두는 이유는 캐시가 실시간 데이터가 아니기 때문이다. 만약 원본 데이터가 바뀐다면 캐시된 내용도 바뀌어야 한다.

캐시를 사용하는 이유

캐시를 사용하면 DB에서 데이터를 읽을 때 발생하는 I/O 오버헤드를 줄일 수 있다. 캐시는 보통 Redis와 같은 In-Memory DB를 사용한다.
In-Memory DB는 메모리를 사용하기 때문에 일반적인 RDBMS보다 데이터를 읽는 속도가 빠르다. 따라서 캐시는 DB에서 데이터를 읽는 것 보다 월등히 속도가 빠르다.

모든 데이터를 캐싱하는 건?

In-Memory DB를 사용하면 요청을 빠르게 처리할 수 있지만, 메모리 특성상 데이터 소실의 위험이 있다. 또한 메모리 사용은 비용이 발생하기 때문에 방대한 양의 데이터를 모두 메모리에 적재하는 것은 큰 비용이 든다.

캐시 설계 전략이 필요한 이유

따라서 데이터의 특성에 따라 DB와 캐시를 적절히 사용하는 것이 매우 중요하다. 캐싱 전략을 올바르게 수립하면 적은 비용으로 큰 퍼포먼스 향상을 기대할 수 있다.


What to Cache

어떤 데이터를 캐싱하는 것이 좋을지 생각해 보아야 한다. 캐싱하기 좋은 데이터의 특성은 다음과 같다.

  1. 자주 바뀌지 않는 데이터
  2. 자주 사용되는 데이터
  3. 자주 같은 결과를 반환하는 데이터
  4. 오래 걸리는 연산의 결과

자주 바뀌지 않는 데이터

자주 바뀌지 않는 데이터의 경우, 한 번 캐시로 저장하면 메모리에서 읽어 빠르게 사용가능하다.
원본 데이터가 바뀌면 캐시도 바뀌어야 한다. 자주 바뀌지 않는 데이터의 캐시는 오랫동안 사용이 가능하기 때문에 캐싱하는 것이 효율적이다.

자주 사용되는 데이터

자주 사용되는 데이터는 캐싱하기 좋다. 한 번 캐싱해 놓으면 캐시를 사용하여 다수의 요청을 효율적으로 처리할 수 있다.
다만, 자주 사용될 지라도 매번 결과 값이 다르면 오히려 캐싱하지 않는 것이 낫다. 만약 일정시간 동안 데이터가 변하지 않는 것이 보장되면 해당 시간만큼의 TTL로 짧은 캐시를 생성하면 된다.
예를 들어 검색처럼 새로 요청하더라도 일정 시간 동안 같은 결과가 반환되는 경우에 해당한다.

자주 같은 결과를 반환하는 데이터

자주 같은 결과를 반환하는 데이터의 경우도 캐싱을 적용하기 좋다. 예를 들어 특정 arguments의 조합에 따라 결과가 일정한 경우 해당된다.
이런 경우는 각 arguments의 조합을 key로 연산 결과를 캐싱하면 된다.

오래 걸리는 연산의 결과

무거운 연산이 반복적으로 계산되어야 한다면 연산의 특성에 맞게 결과를 캐시하는 것이 효율적이다.
또는 사전에 별도의 프로세스에서 작업을 수행하여 결과를 미리 캐싱해 놓을 수 있다.

이 밖에도 일반적인 쿼리나 연산보다 캐싱할 때의 비용이 더 적다면 캐싱을 사용할 수 있다.
로그 분석이나 프로파일링을 통해 캐시를 적용할 함수나 API를 찾을 수 있다. 그러나 모든 사항을 고려하여 캐싱을 적용할지, TTL은 얼마나 적용할지 등은 개발자의 선택이 중요하다.


캐싱 전략 패턴의 종류

캐시를 이용하게 되면 닥쳐오는 문제점이 바로 데이터 정합성의 문제이다. 같은 종류의 데이터라도 두 저장소에 저장된 값이 서로 다른 현상이 일어날 수 밖에 없는 것이다.
따라서 적절한 캐시 읽기 전략(Read Cache Strategy)과 캐시 쓰기 전략(Write Cache Strategy)를 통해, 캐시와 DB간의 데이터 불일치 문제를 극복하면서도 빠른 성능을 잃지 않게 하기위해 연구를 할 필요가 있다.

캐시 읽기 전략 (Read Cache Strategy)

Look Aside Pattern

  • Cache Aside 패턴이라고도 불림.
  • 데이터를 찾을 때 우선 캐시에 저장된 데이터가 있는지 우선 확인, 캐시에 데이터가 없으면 DB에서 조회함.
  • 반복적인 읽기가 많은 호출에 적합.
  • 캐시와 DB가 분리되어 가용되기 때문에 원하는 데이터만 별도로 구성하여 캐시에 저장.
  • 캐시와 DB가 분리되기 때문에 캐시 장애 대비 구성이 되어 있음.
    만일 Cache Store가 다운되더라도 DB에서 데이터를 가져올 수 있어 서비스 자체는 문제가 없음.
  • 대신 Cache Store에 붙어있던 connection이 많았다면, Cache Store가 다운된 순간 DB로 몰려 부하발생 가능.

일반적으로 사용되는 기본적인 캐시 전략. 이 방식은 캐시에 장애가 발생하더라도 DB에 질의를 실행함으로 캐시 장애로 인한 서비스 문제는 대비할 수 있지만, Cache Store와 DB간 정합성 유지 문제가 발생할 수 있음.
반복적으로 동일 쿼리를 수행하는 서비스에 적합, 단건 호출 빈도가 높은 서비스에는 비적합.
이런 경우 DB에서 캐시로 데이터를 미리 넣어주는 작업을 하기도 하는데 이를 Cache Warming이라고 함.

Read Through 패턴

  • 캐시에서만 데이터를 읽어오는 전략 (inline cache)
  • Look Aside와 비슷하지만 데이터 동기화를 라이브러리 또는 캐시 제공자에게 위임하는 방식이라는 차이가 있음.
  • 따라서 데이터를 조회하는데 전체적으로 속도가 느림.
  • 또한 데이터 조회를 전적으로 캐시에만 의지하므로 Cache Store가 다운될 경우 서비스 이용에 차질이 생길수 있음.
  • 캐시와 DB간의 데이터 동기화가 항상 이루어져 데이터 정합성 문제에서 벗어날 수 있음.
  • 읽기가 많은 호출에 적합

Cache Aside 방식과 비슷하지만, Cache Store에 저장하는 주체가 Server인가 혹은 Data Store 자체인가의 차이가 있음.
직접적인 DB 접근을 최소화 하고, Read에 대한 소모되는 자원을 최소화할 수 있음.
하지만 캐시에 문제가 발생하는 경우 바로 서비스 중단이 되기 때문에 Cache Store의 Replication 또는 Cluster구성하여 가용성을 높여야 함.

캐시 쓰기 전략 (Write Cache Strategy)

Write Back 패턴

  • Write Behinde 패턴이라고도 불림.
  • 캐시와 DB 동기화를 비동기하기 때문에 동기화 과정이 생략.
  • 데이터를 저장할 때 DB에 바로 질의하지 않고, 캐시에 모아서 일정 주기 배치 작업을 통해 DB에 반영.
  • 캐시에 모아놨다 DB에 쓰기 때문에 쓰기 커넥션 회수 비용과 부하를 줄일 수 있음.
  • Write가 빈번하면 서 Read를 하는데 많은 양의 리소스가 소모되는 서비스에 적합.
  • 데이터 정합성 확보.
  • 자주 사용되지 않는 불필요할 리소스 저장.
  • 캐시에서 오류발생시 데이터 영구소실의 가능성.

데이터를 저장할 때 DB가 아닌 캐시에 먼저 저장하여 모아놓았다가 특정 시점마다 DB로 쓰는 방식으로 일종의 Queue 역할을 겸하게 됨.
캐시에 데이터를 모았다 한 번에 DB에 저장하기 때문에 DB 쓰기 횟수 비용과 부하를 줄일 수 있지만, 데이터를 옮기기 전에 캐시 장애가 발생하면 데이터 유실이 발생할 수 있다는 단점이 존재.
반대로 DB에 장애가 발생하더라고 지속적인 서비스 제공을 보장하기도 함.
Replication이나 Cluster 구조를 적용하면 Cache Store 서비스의 가용성을 높일 수 있고, Read Through와 결함하변 가장 최근에 업데이트 된 데이터를 항상 캐시에서 사용할 수 있음.

Write Through 패턴

  • DB와 Cache에 동시에 데이터를 저장하는 전략.
  • 데이터를 저장할 때 먼저 캐시에 저장한 다음 DB에 저장.
  • Read Trough와 마찬가지로 DB 동기화 작업을 캐시에 위임.
  • DB와 캐시가 항상 동기화 되어 있어, 캐시의 데이터는 항상 최신 상태로 유지.
  • 캐시와 백업 저장소에 업데이트를 같이 하여 데이터 일관성 유지.
  • 데이터 유실이 발생하면 안 되는 상황에 적합.
  • 자주 사용되지 않는 불필요한 리소스 저장.
  • 매 요청마다 두번의 Write 가 발생함으로 빈번한 생성, 수정이 발생하는 서비스에서는 성능 이슈 발생.
  • 기억장치 속도가 느릴 경우, 데이터를 기록할 때 CPU가 대기하는 시간이 필요하기 때문에 성능 감소.

Cache Store와 DB에 동시에 반영하는 방식. 항상 동기화 되어 있고 항상 최신정보를 가지고 있다는 장접이 있음.
저장할 때마다 2개 과정을 거치기 때문에 상대적으로 느림.

Write Around 패턴

  • 모든 데이터는 DB에 저장
  • Cache Miss가 발생하는 경우에만 DB와 캐시에 데이터 저장
  • DB와 Cache Store의 데이터가 다를 수 있음.

Cache Miss가 발생하기 전에 DB에 저장된 데이터가 수정되었을 때, 사용자가 조회하는 Cache Store와 DB 간의 데이터 불일치 발생.


Cache 읽기 + 쓰기 전략 조합

  • Look Aside + Write Around
  • Read Through + Write Around
  • Read Through + Write Through

캐시 저장시 참고

  • Cache Hit Ratio : 캐시 사용의 정중도. 적중율이 높을 수록 CPU와 주기억장치 속도 차이로 인한 병목현상을 최소화할 수 있다.
    자주 사용되면서 자주 변경되지 않는 데이터를 캐시에 저장할 경우 높은 성능 향상을 이뤄낼 수 있다.
  • 지역성 (Locality) : Cache Hit Ratio는 캐시의 Locality, 즉 지역성에 의해 높아진다. 지역성이란 데이터 접근이 시간적 혹은 공간적으로 가깝게 일어나는 것을 의미함.
    • 시간적 지역성 : 최근에 엑세스 된 프로그램이나 데이터가 가까운 미래에 다시 엑세스 될 가능성이 높은을 의미.
    • 공간적 지역성 : 기억장치 내에 인접하여 저장된 데이터들이 연속적으로 엑세스 될 가능성이 높음을 의미.
    • 순차적 지역성 : 분기가 발생하지 않는 이상 명령어들이 기억장치에 저장된 수서대로 인출되어 실행됨을 의미
  • 일반적으로 캐시는 메모리에 저장되는 형태를 선호한다.
  • 메모리 저장소(Cache Store)는 대표적으로 Redis와 MemCached가 있으며, 메모리를 1차 저장소로 사용하기 때문에 디스크와 달리 제약적인 저장 공간을 사용한다.
  • 자주 사용되는 데이터를 어떻게 뽑아 캐시에 저장하고 자주 사용되지 않는 데이터는 어떻게 제거해 갈것이냐를 지속적으로 고민해야 할 필요성이 있다.
  • 캐시는 자주 사용되며 자주 변경되지 않는 데이터를 기준으로 하는 것이 좋다.
  • 캐시는 휘발성을 기본으로 하기 때문에 어느 정도 데이터 수집과 저장 주기를 가지도록 설계해야 한다.
  • 유실 또는 정합성이 일부 깨질 수 있다는 점을 항상 고려해야 한다.
  • 레파토 법칙 (8:2 법칙)
    전체 결과의 80%가 전체 원인의 20%에서 일어나는 현상을 가리킨다.
    80%의 활동을 20%의 유저가 하기 때문에 20%의 데이터만 캐시해도 서비스 대부분의 데이터를 커버할 수 있다는 의미다.

캐시 제거시 참고

  • 캐시는 기본적으로 영구 저장소에 저장된 데이터의 복사본으로 동작하는 경우가 많다.
  • 데이터 동기화 작업이 반드시 필요하다는 의미로 개발시 고려해야 한다.
  • 캐시 만료 정책이 제대로 구현되지 않은 경우 클라이언트는 데이터가 변경되었음에도 오래된 정보가 캐싱되어 오래된 정보를 사용할 수 있다는 문제점이 있다.
  • 따라서 캐시 구성시 기본 만료정책을 설정해야 한다.
  • 만료 주기가 너무 짧으면 데이터는 너무 짤리 제거되고 캐시의 이점이 줄어든다.
  • 만료 주기가 너무 길면 데이터 변경의 가능성과 메모리 부족현상, 자주 사용해야 하는 데이터가 제거되는 등의 역효과를 나타낼 수 있다.

Cache Stampede 현상

  • TTL 값이 너무 작게 설정될 경우 발생할 수 있는 현상이다.
  • Cache Miss로 DB에 데이터를 요청한 뒤, 다시 Cache Store에 저장하는 과정을 거칠 경우 모든 application에서 DB에서 값을 찾는 duplicate read가 발생한다.
  • 읽어온 값을 각각 Cache Store에 저장하는 duplicate write도 발생하여 처리량도 다 같이 느려질 뿐 아니라 불필요한 작업이 굉장히 늘어나 폭주장애로 이어질 가능성이 있다.

캐시 공유시 참고

  • 캐시는 application의 여러 인스턴스에서 공유하도록 설계한다.
  • 각 application의 인스턴스가 캐시에서 데이터를 읽고 수정할 수 있다.
  • 캐시 업데이트 방식
    1. 캐시 데이터의 변경 직전에 데이터가 검색된 후 변경되지 않았는지 확인해야 한다.
      변경되지 않았다면 업데이트하고 변경되었다면 업데이트 여부를 어플리케이션 레벨에서 결정할 수 있어야 한다.
    2. 캐시 데이터를 업데이트 하기 전에 Lock을 잡는 방식.
      lock을 사용할 경우 조회성 업무를 처리하는 서비스에 Lock으로 인해 대기현상이 발생할 수 있다.

캐시 가용성 참고

캐시를 구성하는 목적은 빠른 성능 확보와 데이터 전달에 있으며 데이터의 영속성 보장하기 위함은 아니다.
데이터의 영속성은 기존 RDBMS에 위임하고, 캐시는 데이터 읽기에 집중하는 것이 성능확보의 지침사항이다.
또한 Cache Store가 장애로 인해 다운 되었을 경우나 서비스가 불가능 할 경우에도 지속적인 서비스가 가능해야 한다. 이는 데이터가 결국 RDBMS에 동일하게 저장되고 유지된다는 점을 뒷바침 한다.

728x90
반응형
 

Head First Design Patterns는 객체지향 프로그래밍과 디자인 패턴에 대한 입문서다. 이 책 또한 다른 전공 서적과 다르게 쉽게 읽히는 책이다. 그림과 예제 코드를 통해 설명되며, 이러한 방식으로 개념을 쉽게 이해할 수 있었다.  
  
"변하지 않는 사실은 계속 변화한다"라는 문장을 책에서 강조하는데, 현업에서 개발하다 보면 기획내용이나 사용자의 요구사항은 계속 변한다. 어제까지는 분명 A를 얘기했는데, 오늘 저녁에 갑자기 B나 C 또는 H로 요구사항이 바뀔 수 있다. 열심히 다 만들었더라도 요구사항이 추가되거나 변하면 새로운 것을 다시 만들어야 하는 상황이 흔한 것이다. 사실 요구사항의 변화는 SW의 본질이라고 할 수 있다. 그렇기에 변경에 용이한 아키텍쳐를 설계하고 개발하는 것이 개발자의 중요 역량이라고 생각한다. 그게 안 되면 IT로 밥 벌어먹으면 안 된다는 생각. 어쨌거나, 변경에 용이한 코드를 작성하기 위해서는 여러 가지 디자인 패턴이 있고, 예제를 패턴을 적용해 개선해 나가는 예시로 설명해준다.

책에서는 디자인 패턴의 개념과 각 패턴이 어떤 상황에서 사용되는지에 대해 설명한다. 디자인 패턴은 각각 객체 생성, 객체 구조, 인터페이스, 행위 등의 다양한 측면에서 소프트웨어 디자인을 개선하기 위한 것이다. 또한, 이 책은 디자인 패턴을 적용하는 과정에서 발생하는 문제와 해결 방법에 대해 다루며, 이를 통해 디자인 패턴을 적용하는 방법을 익힐 수 있도록 돕는다. 디자인 패턴은 사실 무궁무진하게 많다, java, c, javascript 언어에 따라서도 다른 패턴이 나온다. 이 책에서는 java 계열 실무에서 자주 사용하는 디자인 패턴을 집중해서 다뤄주고 있다. 책은 650여 페이지로 굉장히 두꺼운데, 그림이 대다수를 차지하고 있고, 독자의 가독성을 고려하여 쓴 책이라 쉽게 읽힌다. (글을 잘 쓰는 사람이 개발도 잘한다는 말을 반증하는 것 같다.)

나는 객체 지향을 조금 더 잘 활용해보고 싶은 개발자로 추상화, 캡슐화, 다형성, 상속을 어떻게 하면 더 잘 적용할 수 있을지를 이 책을 통해 심도있게 고민해보았다. 흔히들 코드 재사용을 막기 위해 상속을 사용하는데, 생각지 못한 상황에 반복된 코드를 짤 수밖에 없었던 적이 많다. 그런 상황에 대한 해법들도 이 책을 통해 얻었다.
Head First Design Patterns는 객체지향 프로그래밍과 디자인 패턴에 대한 이해를 쉽게 접근할 수 있도록 돕는다. 이 책으로 습득한 디자인 패턴을 사용하면 더 나은 소프트웨어 디자인을 구현할 수 있을 것으로 기대되고, 이는 개발자로서의 역량 향상에 큰 도움이 될 것 같다.

728x90
반응형

채수원 님의 TDD(Test Driven Development) 개발 실천법과 도구는 소프트웨어 개발 방법론 중 하나인 TDD를 구체적으로 실천하는 방법과 그 과정에서 사용되는 도구들에 대한 내용을 다룬 책이다. TDD 대한 이해를 높이고 실제 개발에 적용하는 방법을 설명한다. TDD(Test Driven Development)는 소프트웨어 개발 방법론 중 하나로, 테스트 케이스를 먼저 작성하고 이를 통과하는 코드를 작성하는 것을 중심으로 개발하는 방법

책에서는 TDD의 개념과 원칙을 소개하며 테스트 케이스(TC) 작성, 코드 작성, 리팩토링 등의 단계별로 구체적인 예시를 보여준다. 특히 TC 작성과 이를 통한 코드 작성의 중요성에 대해 강조하며 TDD의 장점과 효과를 다양한 관점으로 보여준다. 책에서는 TDD를 실천하는 데 필요한 기본적인 원칙과 방법론을 설명하면서, 이를 구현하는 과정에서 발생하는 이슈와 해결 방법 등에 대해서도 다룬다. TDD를 적용하는 데에 있어 좋은 습관과 나쁜 습관을 비교하며 TDD를 대하는 개발자의 태도화 실천할 것들을 제시한다.

TDD를 실천하는 과정은 다음과 같다.

  • 테스트 케이스 작성 :
    먼저 개발하려는 기능에 대한 테스트 케이스를 작성. 작성한 테스트 케이스는 해당 기능이 정확히 동작하는지 검증하기 위한 목적으로 작성한다.
  • 테스트 실행 :
    작성한 테스트 케이스를 실행하여 해당 기능이 제대로 구현되어 있는지 확인.
    이때, 테스트 케이스가 실패하면 해당 기능의 구현이 제대로 이루어지지 않았다는 것을 의미.
  • 코드 작성 :
    테스트 케이스를 통과하기 위한 코드를 작성.
    이때, 테스트 케이스가 통과하도록 코드를 작성하는 것이 중요함.
  • 코드 리팩토링 :
    작성한 코드를 리팩토링하여 더 효율적이고 가독성이 좋은 코드로 개선.

이런 과정을 반복하며 개발하는 것이 TDD의 핵심이다. 이 과정에서 개발자는 작성한 코드가 정상적으로 동작하는지 항상 확인할 수 있으며, 버그를 빠르게 발견하고 수정할 수 있다고. 또한, 작성한 코드를 리팩토링하면서 코드의 유지 보수성과 확장성을 높일 수 있다고 한다.

TDD를 실천하는 개발자들은 코드 품질과 안정성을 높일 수 있으며, 개발 프로세스를 보다 효율적으로 관리할 수 있다고 한다. 또한, TDD는 기능 구현 외에도 코드의 문서화와 같은 부가적인 기능을 제공할 수 있어, 소프트웨어 개발의 생산성을 높일 수 있다고 한다.


TDD를 처음 접하는 초심자에게 유용한 정보이지만 초판 이후 어른들의 사정으로 재판이 나오지 않았고, 옛날 기술이나 라이브러리에 대한 설명이 많다. TDD를 업무에서 실 사용해보지 않은 내 입장에서는 일단 겪어봐야 할 것 같다. SW 품질을 높이는데 철학이 없는 조직에서는 TDD를 적용하기가 쉽지 않아서, 어쩌면 뜬구름 잡는 소리로 보이는 것도 있을 것 같다. 그치만 나는 내 일을 하면서 SW 품질을 높이는 데 많은 목마름이 있기에 꼭 TDD를 경험해 봐야 할 것 같다. 일단 나는 사이드 프로젝트에서 TDD를 사용해야겠다.

 
728x90
반응형
  • "나쁜 코드는 깨진 유리창처럼 계속 나쁜 코드가 만들어지도록 한다."
  • "나쁜 코드는 기술 부채를 만들어 수정을 더 어렵게 하며 결국 조직의 생산성을 저하시킨다."

이 책을 추천 받은 것은 개발 쪽으로 전향한 지 얼마 안되서였던 것 같다. 인터넷에서 추천받았었는지, 아니면 국비지원 학원이 끝나갈 무렵이었는지 정확히는 기억나지 않는다. 그리고 최근에 내가 잘 따르던 선배의 강력한 추천이 한번 더 있었다.
어쨌든 토비의 스프링과 함께 한 번은 봐야지 하던 책이었고, 이번에 드디어 완독을 했다. 아니 1독을 했다고 표현해야겠다.

읽코 좋코가 이 책 보다 조금 더 쉽게 읽힌달까. 나중에 후배들을 맞이하고, 혹은 팀을 이끌어갈 때쯤 2독 3독을 해야 할 책이라고 생각된다. 충분히 많은 도움이 된 좋은 책이지만, 내 경험이 짧아 이해하기 어려운 부분도 많고 확실히 내 수준보다는 더 높았다. 아마도 TDD 라던지, 객체지향 패러다임을 잘 따르는 코드를 본 적이 없어서일까?
아무튼 프로세스를 만들 때 가져야 할 마음 가짐에 대해서 많이 생각하게 되었다. 의미를 가진 코드, 코드의 문맥, 추상화 수준과 왜 그렇게 해야 하는지의 이유도 잘 설명되었다. 코드가 마치 잘 쓰여진 소설처럼 읽힐 수 있도록 작성한다는 마음가짐으로 코딩을 하게 되었다. 단순하게 동작이 목적이 아닌 쉽게 읽힐 수 있도록 고민을 하다 보니 더 직관적이고 객체지향 구조로 코드를 짤 수 있었던 것 같다.

선배가 이 책을 추천할 때, 레거시 코드에 대해 정말 화를 내고 열변을 토했던 것으로 기억한다. 그리고 이 책을 읽으면서 그가 이 책을 추천하면서 왜 그렇게 화를 냈는지 납득했다. 저자는 나쁜 코드가 나쁜 이유에 대해서 여러 예를 들고 그중 기억에 남는 것은 위의 두 문장이다.

나 역시도 나쁜 코드를 혐오한다. 레거시 코드를 유지보수하면서 소스코드의 히스토리를 보면 왜 이런 코드가 생겼는지 납득할 수 있지만, 그렇다면 처음부터 더 좋은 구조로 작성될 순 없었는가 하는 의문이 들곤 한다. 솔직하게 말해서 나쁜 코드를 보면 화도 나고 작성자를 기둥에 묶어두고 싶다는 생각도 든다.
나를 포함한 많은 개발자들이 이 책을 읽고 객체지향 패러다임과 클린코드 원칙들을 따르는, 개발을 잘하는, 후배들에게 좋은 코드를 남기는 시니어가 될 수 있도록 갈고닦기를... 🙏🏻

 
728x90
반응형

읽코좋코

모든 전공 서적들이 빠르게 읽히지 않는데, 다른 전공서적에 비해 쉽게 읽히는 책이며 많은 공감을 하게 되었다. 제목과 내용은 동일하며 주제를 반복 강조하고 있다. 이를 위해 변수명, 함수명, 코드 컨벤션 그리고 리팩토링까지 설명하고 있다.
코드는 이해하기 쉬워야 한다, 다시 말해 코드를 읽는데 드는 비용을 줄여야 한다는 말이다. 코드 파악하는데 드는 비용이 작다는 말은 설계가 명확하고 간결하다는 의미를 내포한다.

이 책을 읽고 실제 코드로 적용할 생각을 하면 머리가 아프지만 읽는 중간 중간 이전에 보았던 코드들이 떠올랐다. 이전에 작성 했던 '성능과 가독성을 높이는 분기처리 방법'을 쓸 때 보다 시야가 더 넓어졌다. "왜?"에 대한 부분이 조금 모호했었는데, 그 부분을 채워주는 내용이였다. 그리고 내가 봐온 레거시 프로젝트들의 코드를 작성했던 개발자들에게 이 책을 던져주고 싶었다.


  1. 코드는 이해하기 쉬워야 한다
    인상 깊었던 내용은 6개월 뒤의 나 자신 또한 쉽게 이해되도록 코드를 작성하라는 내용이였다. 나 역시도 나름의 설명과 주석을 잘 붙여서 만들었다고 생각했지만 1년 뒤 해당 코드를 개선 하면서 다시 분석해 보는 헛수고를 했던 경험이 있었다. 짧은 코드가 더 좋은 코드가 아닌 읽는 순간 바로 이해되는 코드가 좋은 코드라는 뜻이다.
  2. 이름에 정보 담기
    • 구체적인 단어 사용하기
    • 이름 길이(짧은 범위에 잠깐 쓰이면 짧은 변수명, 사용 범위가 넓다면 긴 변수명, 약어 사용 지양하기 등)
    • 포맷팅으로 의미 전달하기(상수는 대문자, 클래스명은 카멜케이스 등)가 있었다.
  3. 오해할 수 없는 이름들
    • 경계를 포함하는 범위 : first, last
    • 경계를 포함/배제하는 범위 : begin, end 사용
    • 불린 : is, has, can, should 사용
    • get 남발하지 않기
    • 여러 개의 이름을 후보로 놓고 고민하기
    • '다른 사람들이 다른 의미로 해석할 여지가 있는가?'가 핵심이였다. 한 번쯤 더 생각해 보면 더 좋은 변수명을 만들 수 있다.
  4. 미학
    • 문서 작업을 하더라도 한눈에 보기 쉽게 쓰인 글이 잘 읽히듯 코드도 마찬가지다.
    • 좌우로 길게 늘어진 코드는 줄 바꿈을 하고 다른 코드들도 똑같이 맞추기
    • 헬퍼 메소드를 이용하여 지저분한 코드 깔끔하게 하기
    • 코드의 위아래 열을 맞추어 읽기 쉽게 하기
    • 코드의 순서를 '가장 중요한 것'에서 '가장 덜 중요한 것' 까지 순서대로 나열하기
    • 코드를 문단으로 만들기 (핸들러, DB 사용 등 문맥이 바뀌면 한 행을 띄우는 등 글을 쓸때 문단을 나누는 것과 동일)
    • 개인의 코드 스타일 VS 전체 코드의 일관성 = 일관성이 올바른 스타일보다 더 중요
  5. 주석에 담아야 하는 대상
    • 불필요한 설명 배제
    • 나쁜 이름에 대한 변명이 아니라 좋은 이름으로 바꿀 것
    • 작업중 생각한 통찰 등을 주석에 기록
      • ex: 하위클래스로 정리해야 할 것 같다
    • 상수의 의미를 설명할 것
    • 다른 개발자가 실수할 수 있는 부분을 예상해서 주석을 달라
      • ex : 1분 후 타임아웃 된다
    • 2, 3중 반복/조건문이 달린 함수는 무엇을 위한 것인지 요약 주석을 단다
    • 주석을 다는 것에 대한 두려움을 버려라
  6. 명확하고 간결한 주석 달기
    • 간단한 입출력 예시 사용
    • 코드의 수행 동작이 아니라 코드의 의도를 적어라
    • 파라미터에도 주석을 넣어라
    • 축약된 단어를 사용하라
      • ex : ~에서 불필요한 빈칸을 제거한다 -> 공백 제거
  7. 읽기 쉽게 흐름 제어 만들기
    • 조건문에서 유동적인 값은 왼쪽, 상대적으로 고정적인 값은 오른쪽에 쓰라
    • if/else 간단한 것을 먼저 처리하라
    • 삼항 연산자의 사용은 간단하게 쓸 상황이 아니라면 피하라
    • 중첩을 피하기 위해 함수를 중간에서 반환하라
    • 2중 if 문은 나누어서 중간에 반환하라
  8. 거대한 표현 잘게 쪼개기
    • 요약 변수 사용, 조건식의 반복되는 중요 비교 값을 상수로 만들기
    • 드모르간의 법칙 사용
    • short circuit 사용을 오용하지 말 것
      • 영리하게 작성된 코드가 혼란을 초래한다
    • 복잡한 논리를 간단하게 표현하기
    • 거대한 구문은 문맥에 따라 나누기
  9. 변수와 가독성
    • 불필요한 임시 변수, 중간 결과 저장 변수, 흐름 변수(boolean) 제거하기
    • 변수의 범위 좁히기
    • 클래스를 작은 단위로 나누기
    • 전역변수 사용 피하기
    • 변수에 다른 값을 여러번 할당하는 것 피하기
  10. 상관 없는 하위 문제 추출하기
    • 비즈니스 로직과 관계 없는 문자열 빈칸 제거, url 형식, 주소 형식등을 다루는 메소드는 분리하기
    • 재사용성을 위해 일반적인 목적을 가진 코드를 많이 만들기
    • 과유불급, 지나치게 나누지는 말기
  11. 한 번에 하나씩
    • 작성된 코드가 읽기 어렵다면, 수행하는 작업을 나열하라
    • 나열된 작업중 분리될 수 있다면 별도의 함수 또는 클래스로 나눠라
    • 파편화를 최소화 할 수 있다.
  12. 생각을 코드로 만들기
    • 논리를 명확하게 설명하기
    • 해결책을 말로 묘사하기
    • 언어에서 제공하는 라이브러리를 많이 사용해 보기
  13. 코드 분량 줄이기
    • 필요가 없는 기능을 구현하려고 애쓰지 말것
    • 코드 베이스를 작게 유지할 것
    • 요구사항에 질문을 던져 요구사항을 잘게 나누어 분석하기.
    • 표준라이브러리와 친해지기
  14. 테스트와 가독성
    • 테스트 코드는 특히 더 읽기 쉬워야 한다
    • 긴 테스트 내용은 다른 메소드로 만들기
    • 읽기 편한 에러 메세지 출력 폼 만들기
    • 좋은 테스트 입력 값 선택하기
    • 지금 작성하는 코드의 테스트 코드를 나중에 작성한다는 사실을 염두해 둘것
    • 지나친 테스트는 지양
  15. 분/시간 카운터 설계 및 구현
    • 실습 예제
 
728x90
반응형

토비의 스프링 3.1 VOL.1

스프링 사용자로서 항상 읽어야봐야 한다라는 소리에 두권을 샀지만, 엄청 어렵다는 소문에 읽을 엄두를 못 내던 책이다. 한 권에 800 페이지가 넘는 굉장한 분량에 두번의 정독 시도를 헛탕쳤었다. [유튜브] "개발바닥" 채널에서 저자 이일민님의 인터뷰와, 그 분이 이제는 스프링 부트 인강을 내시면서 이 책을 다시 시도하였고. 이번에는 1독을 끝냈다.
이제 4년차에 들어서면서 읽어보니 '어느 정도 개발을 했고, 좋은 구조와 효율을 고민해본 사람이 봐야 깊은 뜻을 더 잘 이해할 수 있겠구나' 라고 느꼈다.

이 책은 객체지향적 설계, 리팩토링, 테스트주도개발의 중요성에 대해 꾸준히 강조하고 있다. 단순하게 스프링 프레임워크의 기능 / 개념에 대한 설명도 깊게 나와있지만, 이 책의 요점은 스프링이 왜 이렇게 발전했고, 어떤 패러다임을 녹여서 만든건지 엿볼 수 있도록 도와주는 책이다. 따라서 개발 철학에 더 중점을 두고 읽어야 하는 책이라고 생각 된다.

마틴 파울러와 켄트 백이 강조하던 내용이 녹아 있으며, 아래와 같은 조언을 책 전반에 걸쳐 꾸준히 강조하고 있다.

  • '고정된 작업 흐름을 갖고 있으면서 여기저기서 자주 반복되는 코드가 있다면, 중복되는 코드를 분리할 방법을 생각해보는 습관을 기르자.'
  • '비슷한 기능이 새로 필요할 때마다 앞에서 만든 코드를 복사해서 사용할 것인가? 물론 아니어야 한다. 한두 번까지는 어떻게 넘어간다고 해도, 세번 이상 반복된다면 본격적으로 코드를 개선할 시점이라고 생각해야 한다.'

인상 깊었던 문장

인상 깊었던 문장들은 다음과 같다. 다 읽어보면 떠오르는 것이 있다. 그렇다, 대부분의 레거시 프로젝트의 소스들은 아래에서 하지 말라는 대로 개발되어 있다.

  계층형 아키텍쳐
  관심, 책임, 성격, 변하는 이유와 방식이 서로 다른 것들을 분리함으로써 분리된 각 요소의 응집도는 높여주고 서로 결합도를 낮춰줬을 때의 장점과 유익이 무엇인지 살펴봤다. 성격이 다른 모듈이 강하게 결합되어 한데 모여 있으면 한 가지 이유로 변경이 일어날 때 그와 상관이 없는 요소도 함께 영향을 받게 된다. 따라서 불필요한 부분까지 변경이 일어나고 그로 인해 작업은 더뎌지고 오류가 발생할 가능성이 높아진다. 어느 부분을 수정해야할지를 파악하기도 쉽지 않다.
  따라서 인터페이스와 같은 유연한 경계를 만들어 두고 분리하거나 모아주는 작업이 필요하다.
또, 흔히 저지르는 실수 중의 하나는 프레젠테이션 계층의 오브젝트를 그대로 서비스 계층으로 전달하는 것이다.
서블릿의 HttpServletRequest나 HttpServletResponse, HttpSession 같은 타입을 서비스 계층 인터페이스 메소드의 파라미터 타입으로 사용하면 안 된다.
계층의 경계를 넘어갈 때는 반드시 특정 계층에 종속되니 않는 오브젝트 형태로 변환해줘야 한다.
스프링을 사용하면 이런 데이터 중심의 코드를 만들 수 있을 뿐만 아니라, 실제로 매우 흔하게 발견된다.
데이터와 업무 트랜잭션 중심의 개발에 익숙한 사람들이 많고 이런 아키텍쳐를 의도적으로 선호하는 개발자도 많기 때문이다.
개발자들끼리 서로 간선없이 자신에게 할당된 기능을 독립적으로 만드는 데도 편하다.
최소한의 공통 모듈 정도만 제공되는 것을 사용하고, 그 외의 기능은 단위 업무 또는 웹 화면 단위로 만들어 진다.

하지만 이런 개발 방식은 변화에 매우 취약하다.
객체지향의 장점이 별로 활용되지 못하는데다 각 계층의 코드가 긴밀하게 연결되어 있기 때문이다.
중복을 제거하기도 쉽지 않다.
업무 트랜잭션에 따라 필드 하나가 달라도 거의 비슷한 DAO 메소드를 새로 만들기도 한다.
또한 로직을 DB와 SQL에 많이 담으면 담을수록 점점 확장성이 떨어진다.
DB는 확장에 한계가 있을 뿐 아니라 확장한다 하더라도 매우 큰 비용이 든다.
잘 작성된 복잡한 SQL 하나가 수백 라인의 자바 코드가 필요한 비지니스 로직을 한번에 처리할 수도 있다.
하지만 과연 바람직한 것일까?
이런 복잡한 sql을 누구나 쉽게 이해하고 필요에 따라 유연하게 변경할 수 있을까?
또, 복잡한 sql을 처리하기 위해 제한된 자원인 DB에 큰 부담을 주는 게 과연 바람직한 일인지 생각해볼 필요가 있다.
데이터 중심 아키텍쳐의 특징은 계층 사이의 결합도가 높은 편이고 응집도는 떨어진다는 접이다.
화면을 중심으로 하는 업무 트랜잭션 단위로 코드가 모이기 때문에 처음엔 개발하기 편하지만 중복이 많아지기 쉽고 장기적으로 코드를 관리하고 발전시키기 힘들다는 단점이 있다.
스프링은 그 개발철학과 목표를 분명히 이해하고 사용해야 한다.
자바의 근본인 OOP 원리에 충실하게 개발할 수 있으며,
환경이나 규약에 의존적이지 않은 POJO를 이용한 애플리케이션 개발은
엔터프라이즈 시스템 개발의 복잡함이 주는 많은 문제를 해결할 수 있다.
POJO 방식의 개발을 돕기 위해 스프링은 IoC/DI, AOP, PSA와 같은 기능 기술을 프레임워크와 컨테이너라는 방식을 통해 제공한다.
728x90
반응형

1. Debug Mode 활용

디버그 모드로 애플리케이션을 구동시키면 아래 스크린샷과 같이 변수, 객체 등의 값을 알 수 있다. log를 찍거나 system print 등을 사용하지 앖아도 실시간으로 값을 알 수 있고 브레이크 포인트를 사용해서 트래킹 하는데 수월하니 앞으로는 IDE의 디버그 모드를 적극 활용하자. intellijeclipse 모두 지원한다.

IntelliJ의 디버그 모드


2. Comment 제거

아래 기준을 참고해서 과감하게 기존 코멘트를 제거한다.

  • 위 디버그 모드 활용과 더불어 단순 값 확인용 코멘트는 제거한다.
  • 의미없는 ‘//////////////’ 와 같은 구분선 종류의 코멘트는 제거한다.
  • Github을 통해 트래킹, 복원이 쉽기 때문에 불필요한 주석은 과감하게 제거한다.

3. 선언, 초기화, 디폴트에 신경쓰자

코드 품질은 디테일에서 나온다. 그리고 디테일은 기본기 없이 챙기기 힘들다. 아래 스크린샷을 보자.

  • 선언부 이후에 로직 중에 항상 해당 변수에 값을 할당한다면 굳이 선언하면서 초기화 할 필요가 없다.
  • 의미없는 값으로 대충 초기화 하는 습관은 멀리하자. 초기화 하는 값은 일반적으로 디폴트 값이어야 하고 이 디폴트 값이 뭔지 정확히 알고서 초기화 해야한다.

4. 가독성을 높이자

// Before
// 결제상점아이디에 따른 분기
if (TRD_NO.indexOf(oldIdPart) >= 0) {
    sMallID = oldMid;
}

// After
if (TRD_NO.contains(oldIdPart)) {
    sMallID = oldMid;
}

위 코드는 파라미터로 넘긴 string의 존재 여부를 검사한 뒤 그에 따른 처리를 하기위한 것으로 보인다. 그래서 자바의 String에 있는indexOf() 메소드를 사용해 >= 0 조건으로 판별한다. 이 코드는 기능상 아무 이상 없이 작성자의 의도대로 잘 작동한다.

앞서 이야기한 것처럼 이런 코드에서 보이는 디테일을 잡으면 가독성과 품질이 상승한다.

indexOf 와 contains

자바에는 각 객체를 위한 유틸 메소드들이 많다. 그 중 contains라는 메소드로 indexOf를 대체할 수 있다. contains의 내용은 아래 스크린샷처럼 indexOf를 래핑한 메소드다. 따라서 성능이나 기능에선 차이가 없다고 봐도 된다.

JDK 1.5에 생긴 contains
indexOf

그럼 왜 indexOf를 contains로 대체하려고 하는거고 언제 써야 할까?

indexOf와 contains는 용도가 다르다.

indexOf는 스트링이 시작되는 index를 int값으로 반환하고 contains는 스트링의 포함 여부를 판단해 boolean으로 반환한다. 따라서 특정 비즈니스 로직 혹은 알고리즘을 구현하기 위한 경우가 아니면 일반적인 경우에 contains의 목적으로 더 많이 쓰게 된다.

Readability

물론 indexOf를 보고 >= 0 조건을 보면 뭘 하려는지 알 수 있다. 하지만 contains라는 단어가 그 모든걸 포함하기 때문에 훨씬 더 직관적이다. contains쓰면 indexOf라는 메소드는 잘 썼는지, 뒤에 >=0 인지 > 0 인지 > -1 인지 잘못쓰진 않았는지 이런 생각 할 필요도 없다. 가독성을 정의할 때 그저 문자가 잘 읽히는 정도를 넘어서 (특히 메소드는) 이름에 맞는 목적과 기능을 신뢰할 수 있어서 불필요한 생각도 줄여주는 수준까지 갈 수 있도록 신경써야 한다.


5. Naming - 변수명은 중요하다

현재 레거시 코드의 문제점

  • 
프론트 HTML tag'name을 그대로 백엔드 로직에 사용함
  • 마크업과 프론트 사정에 따른 hChk, pChk 와 같은 변수명 백엔드에서 실제 용도를 구분하기 어려움
  • 변수명과 주석이 일치하지 않거나 주석의 뜻을 담아내기에 부족한 변수명이 많음

실제 주문취소에서 볼 수 있는 케이스는 아래와 같다.

    // bad case
    String[] hChk = request.getParameterValues("hChk");//선택여부

    // good case
    String[] cancelSelectYn = request.getParameterValues("hChk"); // 취소선택여부

위 변수가 가리키고 있는 건 주문 취소 시 상품 별 체크박스 값이다.(아래 스크린샷) 따라서 hChk 라는 이름을 그대로 백엔드 로직에도 차용한다면 코드 전체를 트래킹 해야 하는 수고가 더해진다. 그래서 이름을 취소선택여부, 취소신청여부 등으로 하고 변수명도 이에 맞춰 수정하는 것이 좋다.

주문 취소 상품 선택화면

하지만 실제로 레거시 시스템은 볼륨도 크고 복잡한 상호관계를 이미 가지고 있는 상태기 때문에 변수명만 바꾸기에 위험하다. 래거시 프로젝트 소스코드도 마찬가지이며 따라서 변수명을 바꾸려 할 땐 아래 항목을 점검해서 진행한다.

  • JSP와 JAVA에서 동일하게 사용하고 있는 변수가 response나 query에서 반드시 동일하도록 짜여 있는지
  • 주석과 변수명이 다르다면 둘 중 어느 것이 맞는지, 둘 다 틀린지
  • 변수명을 바꿨을 때 영향 범위가 백엔드 비즈니스 로직에만 해당되는지

6. 조회와 요청, 트랜젝션

  1. 사용자의 화면에 보이는 내용은 화면을 그리는 시점에 유효한 정보이다(화면을 실시간으로 갱신하지 않는한)
  2. 현재 프로세스는 <step 2> 와 같고 주문 취소 요청 시 JSP에 뿌려진 주문/결제 값으로 환불요청을 시작한다
  3. 때문에 주문 조회 시점과 환불 요청 시점 차이가 있을 때 주문 상태 차이가 발생할 수 있고 중복 발생 가능성 또한 있는 구조다.
  4. 개선 프로세스는 <step 3> 와 같이 사용자 요청을 받고 현재 주문과 환불 진행상태 등을 DB에서 조회하는 ② 부터 완료된 주문/결제/환불 정보를 DB에 업데이트하는 ⑥ 까지를 하나의 트랜잭션으로 묶는다.

대부분의 레거시 프로젝트 코드에는 화면에서 가져온 값들로 무언가 처리하는 로직이 많은데 전반적인 수정이 필요하다.


7. 3-tier Architecture

아래 쿼리를 먼저 보자.

// 이런 패턴
, X.BANK_CD                <!--은행코드-->                        
, CASE WHEN X.BANK_CD IS NOT NULL
  THEN DECODE(X.BANK_CD, '02', '산업', '03', '기업', '05', '외환', '06', '국민', '07', '수협', '11', '농협', '20', '우리', '23', 'SC제일', '27', '한국씨티', '31', '대구'
                  , '32', '부산', '34', '광주', '35', '제주', '37', '전북', '39', '경남', '45', '새마을금고', '48', '신협', '71', '우체국', '81', '하나', '88'
                  , '신한(계좌이체)', '26', '신한(가상계좌)','S0', '동양증권', 'S1', '미래에셋', 'S2', '신한금융투자', 'S3', '삼성증권', 'S6', '한국투자증권' , 'SG', '한화증권')
  ELSE '-'
  END    BANK_NM            <!--은행명-->

// 비슷한 패턴
, X.MEMO                <!--메모(가상)-->
, NVL(X.MEMO, 'X') SHW_MEMO                <!--화면용메모(가상)-->

// 비슷한 패턴
, X.RECP_PSN_TELNO            <!--수령인전화번호-->
, CASE WHEN  LENGTH(REPLACE(NVL(X.RECP_PSN_TELNO, 'X'), '-', '')) <![CDATA[ < ]]> 9
  THEN 'X'
  ELSE X.RECP_PSN_TELNO
  END   SHW_RECP_PSN_TELNO            <!--수령인전화번호-->

안좋은 케이스로 보이는 건..

  • 쿼리 안에서 데이터 가공을 한다는 것이다. 게다가 화면에 보여줄 목적인 것들이 꽤 있다.
  • 코드 관리나 관련 메소드가 애플리케이션에 있는게 아니라 쿼리에 들어가 있다.

대부분의 레거시 프로젝트들은 JSP, Spring, Oracle 기반이지만 잘 구분된 계층 구조를 이루고 있지 않다. 해당 계층에서 해야 할 일들이 다른 계층으로 번져간다면 결국 문제가 생길때마다 화면~데이터 모든 영역의 코드를 살펴봐야만 어느 부분이 문제인지 찾아낼 수 있다. 그래서 앞으로 신규 개발하는 화면, 기능은 이러한 강한 결합을 피하고 우리 시스템과 기술이 지향하는 3티어 계층에 맞춰 프로그래밍을 하도록 한다. 추후 가이던스를 마련하고 공유하겠지만 먼저 간단하게 요약하면 아래와 같다.

  • 화면 코드에(JSP)에 비즈니스 로직 넣지 말자
  • 쿼리에 비즈니스 로직, 하드코딩, 화면 표시용 글자 넣지 말자(특수한 경우 제외)
  • 컨트롤러에 비즈니스 로직 넣지 말자
  • 실제 필요한 것들만 파라미터로 전달하고 용도에 맞는 모델을 만들자

 


출처 : 같이 일 했던 선배가 컨플루언스에 올린 글

 
728x90
반응형

+ Recent posts