좌충우돌 개발

2017-12-18_TIL

|

2017년 12월 18일.

IntelliJ 사용 시 직접 웹브라우저를 실행하지 말고 왼쪽 작업 디렉토리에서 해당 페이지를 우클릭해서 열면 페이지 변경 시 자동 반영된 것이 보인다.

2017-11-26_TIL

|

2017년 11월 26일

프로그래머의 길, 멘토에게 묻다.

프로그래머로서 주요 업무를 운영/개발로 맡아 왔지만, 근 몇년간 업무로 개발을 하지 않았다.

어떻게 앞으로 해야할 지 궁금하던 차에 알게되어 읽기 시작하였다.

숙련된 프로그래머를 위한 책이 아닌 이제 막 이 분야에 진입하거나 아직 프로그래밍 실력이 부족한 이에게 방향을 잡아주는 데 목적을 두고 있는 책이다.

  • 잔을 비워라
    • 첫 번째 언어
    • 흰 띠를 매라
    • 열정을 들어내라
    • 구체적인 기술
    • 무지를 드러내라
    • 무지에 맞서라
    • 깊은 쪽
    • 한발 물러서라

REST API

|
기술 구성 : HTTP + JSON
구성

Resource : 명사형으로 표현한다
Message : http body 담아서 보낸다
Method : http method
POST : CREATE
GET : SELECT (Idempotent)
PUT : UPDATE (Idempotent)
DELETE : DELETE (Idempotent)

장점
  • 상태정보를 관리하지 않는다.
  • Uniform Interface : HTTP + JSON 구조이면 어떤 플랫폼을 사용 가능
  • Http의 캐시 기능 활용
  • Self-descriptiveness : API 메시지만 보고도 이해할 수 있는 구조
  • 클라이언트 서버 구조
  • 계층형 구조 : ex) 마이크로 서비스 아키텍처
단점
  • RDBMS에 맞추기가 어렵다 : 복합KEY 사용
  • 표준이 없다
REST API 디자인 가이드
  1. REST URI는 단순하고 직관적으로 만들자
  2. 리소스 간의 관계를 표현하는 방법
    • 서브 리소스로 표현하는 방법 : /users/terry/dogs
    • 서브 리소스에 관계를 표현하는 방법 : /users/terry/has/dogs
  3. 에러 처리
    • 200 - 성공
    • 400 Bad Request - field validation 실패 시
    • 401 Unauthorized - API 인증, 인가 실패
    • 404 Not found - 해당 리소스 없음
    • 500 Internal Server Error - 서버 에러
  4. 버전관리
     {servicename}/{version}/{REST URL}  
    

    외부에서는 api url로 통해 들어오고 reverse proxy를 사용하여 URL 매핑을 사용 한다. 외부 노출된 URL변경이 없이 향후 확장되었을 때 서버를 물리적으로 분리해내기 편리하다.

  5. 페이징
    Record?offset=100&limit=25
  6. 부분 응답 처리
    API를 요청하는 클라이언트의 용도에 따라 선택한 필드만을 보내서 전체 네트워크의 대역폭을 절약할 수 있다.
     /terry/friends?field=id,name  
    
  7. 검색(전역 검색과 지역 검색)
    검색은 HTTP GET에서 쿼리 스트링 검색 조건을 정의하는 경우가 일반적인데 , 이 경우 검색 조건이 다른 쿼리 스트링과 섞여 버릴 수 있다.
    URLEncode를 써서 표현하고, 구분자를 사용하게 되면 검색 조건은 다른 쿼리 스트링과 분리된다. 이 검색 조건은 서버에 의해서 토큰 단위로 파싱 되어야 한다
    전체 검색: /search?q=id={}
    Resource 검색: /resource?q=id={}
  8. HATEOS를 이용한 링크 처리
    Hypermedia as the engine of application state의 약어로, 하이퍼미디어의 특징을 이용하여 HTTP 응답에 다음 액션이나 관계된 리소스에 대한 HTTP 링크를 함께 반환하는 것이다.
  9. 단일 API 엔드포인트 활용
    Reverse Proxy
REST API 보안
  1. REST API 보안 관점
    • 인증(Authentication) : 누가 서비스를 사용하는지를 확인하는 절차
    • 인가(Authorization) : 해당 리소스에 대해서 사용자가 사용권한이 있는지 확인 하는 과정
    • 네트워크 레벨 암호화 : 일반적으로 HTTPS를 사용한다
    • 무결성 보장 : 변조 방지
    • 메시지 본문 암호화 : 전체 암호화 보다는 특정 필드만 암호화를 주로 사용한다.
  2. 인증
    • API 키 방식 : 모든 클라이언트가 같은 키를 공유함으로 권장하지 않는다
    • API 토큰 방식 : 아이디와 비밀번호로 사용자를 인증한 후 API를 호출에 사용할 기간이 유효한 API토큰을 발급하여 API 토큰으로 사용자를 인증하는 방식 HTTP
    • Basic Auth : 사용자 아이디와 비밀번호를 Base64로 인코딩하여 http 헤더에 저장하여 서버에 전송하여 인증하는 방식이다. 중간에 패킷을 가로채면 사용자 정보가 노출됨으로 반드시 https로 통신 하여야 한다. Digest Access Authentication : 클라이언트가 인증을 요청할 때 클라이언트가 서버로부터 nonce이라는 일종의 난수 값을 받고(서버와 클라이언트는 이 난수값을 서로 알고 있음), 사용자 ID와 비밀번호를 이 난수 값을 이용해서 해시화 하여 서버로 전송하는 방식이다.
    • 클라이언트 인증 추가
    • 제3자 인증 방식 (Oauth 2.0 Authorization grant type) : ex)페이스북 인증
    • IP 화이트 리스트를 이용한 터널링
    • Bi-directional Certification (Mutual SSL) : 클라이언트에도 인증서를 두고 SSL 통신하는 방식
    • JWT 방식(JSON Web Token)
      • Claim 기반 토큰의 개념 : Oauth의 access_token은 특별한 정보를 담지 않은 랜덤 문자열
      • Claim : 사용자에 대한 property나 속성
      • Oauth 1.0과 JWT의 차이점 : Oauth1.0에서 발급한 토큰과 권한 정보를 매핑한 정보를 서버 측에 저장하고 있다 요청이 들어오면 토큰과 저장되 토큰을 비교 하여 resource룰 반환 한다. JWT에서는 토큰에 사용자 아이디와 권한 정보를 담아 base64로 인코딩하여 클라이언트로 보내고 서버에는 토큰 정보를 가지고 있지 않는다. 요청이 들어오면 토큰내의 정보를 가지고 resource를 반환여부를 판단한다.
    • JWT 소개
      • Claim (메시지) 정의 : Claim에는 줄 바꿈이 있기 때문에 base64로 인코딩하여 하나의 문자열로 만든다.
      • 변조 방지 : 변조 방지를 위하여 HMAC을 사용한다.
      • 서명 생성 방식
      • 전체 메시지 포맷
    • JWT 문제점
      • 길이 : claim이 길어질수록 토큰 길이도 길어진다. 호출 시 마다 사용됨으로 대역폭 낭비가 발생한다.
      • 한번 발급된 토큰은 값을 수정하거나 폐기가 불가 : 만료시간을 명시적으로 둔다. Refresh Token등을 이용해서 중간마다 토큰을 재발행 한다.
      • 암호화 : 기본적으로 Claim에 대한 정보를 암호화 하지 않는다. 토큰자체를 암호화 하는 방법은 JWE(JSON Web encryption)이다
  3. 권한 인가
    API 인가 방식
    - RBAC(Role Based Access Control) : Role로 권한을 묶고 role을 부여하는 방식
    - ACL(Access Control List) : 직접 권한 부여 방식

    API 권한 인가 처리 위치
    - 클라이언트에 의한 API 권한 인가 처리
    - 게이트웨이에 의한 권한 인가 처리
    - 서버에 의한 API 권한 인가 처리 : 가장 일반적이고 보편적인 인가 방식

    네트워크(전송) 레벨 암호화
    - Man in the middle attack : 해커는 인증기관의 인증서를 사용 할 수 없으므로 반드시 서버의 인증서를 확인 하도록 한다. 메시지 본문 암호화
    - RSA 알고리즘
    메시지 무결성 보장
    - HMAC에 timestamp를 사용 하여 무결성 및 재사용 공격을 방어한다.

  4. 자바스크립트 클라이언트 지원
    SPA(Single Page Application) : 브라우저간 이동 없이 자바스크립트를 이용해서 동적으로 페이지를 변경할 수 있는 방식

    SPA의 경우 서버와 통신을 자바스크립트가 직접 XMLHttpRequest 객체를 이용해서 API를 호출 하는 형태

    동일 출처 정책에 대한 처리
    siteA.com에서 로딩한 스크립트에서 api.my.com으로 API를 XMLHttpRequest를 호출하게 되면 동일 출처 정책에 의해서 호출 에러 발생

    프록시를 이용한 방식
    Reverse Proxy를 넣어서 전체 URL을 같게 만든다.

    특정 사이트에 대한 접근 허용 방식
    HTTP 요청의 응답을 주면서 HTTP 헤더에 Request Origin(요청을 처리해 줄수 있는 출처)를 명시 해준다.

      Access-Control-Allow-Origin: sitea.com => sitea.com만 허용
     Access-Control-Allow-Origin: * => 모든 사이트 허용
    

    Pre-flight를 이용한 세세한 CORS 통제
    웹 브라우저에서는 XMLHttpRequest를 특정 URL에 요청하기 전에 먼저 HTTP OPTIONS를 호출한다. 그러면 서버는 해당 URL에 접근할 수 있는 Origin URL과 HTTP 메서드를 반환해 준다. 이를 Pre-flight 호출이라고 하는데, 이 정보를 기반으로 브라우저는 해당 URL에 XMLHttpRequest를 보낼 수 있다.

    자바스크립 클라이언트를 위한 API Access Token에 대한 인증 처리

    API Access Token을 보안 쿠키를 통해서 주고 받는다.
    API Access Token은 해당 세션에서만 유효하도록 한다.

구현 설계

|

Chapter 5 구현 설계

내용

  • 5.1. 설계의 문제점
  • 5.2. 설계의 핵심 개념
  • 5.3. 빌딩 블록의 설계
  • 5.4. 설계 방법
  • 5.5. 유명한 방법론에 대한 의견

5.1. 설계의 문제점
설계에는 완벽은 없다 주어진 상황에 맞추어 진행되며 항상 완료될때까지 발견적 학습과정이다.

5.1.1. 설계는 불명확한 문제이다. (문제를 해결해나아가는 과정이다)
5.1.2. 결과는 정돈되었을 지라도, 설계는 엉성한 프로세스이다. (완벽하지는 않다.)
5.1.3. 설계는 트레이드오프와 우선순위에 관한 것이다.
5.1.4. 설계에는 제약이 따른다.
5.1.5. 설계는 다양성을 지닌다.
5.1.6. 설계는 발견적 학습 과정이다.
5.1.7. 설계는 창발적이다.

5.2. 설계의 핵심 개념
훌륭한 설계는 중요한 개념들을 얼마나 잘 이해하는지에 달려있다.

  • 이해를 돕기 위해 비유를 사용한다
  • 개념을 이해하고 단순화 시킨다.

5.2.1. 소프트웨어의 기본적인 기술적 의무 : 복잡성관리
본질과 비본질
우리가 한번에 한 부분을 제대로 집중할 수 있게 프로그램을 구성하도록 노력해야 한다. (단순화)
소프트웨어 아키텍처 수준에서는 시스템을 서브시스템으로 나누어 문제의 복잡성을 줄인다.

  • 한번에 처리해야하는 본질적인 복잡성의 양을 최소화 한다.
  • 비본질적인 복잡성이 불필요하게 증가하지 않도록 한다.

5.2.2. 바람직한 설계의 특징

  • 복잡성 최소화: 재치있는 설계보다는 간단하고 이해하기 쉬운 설계로 만들어라.
  • 유지 관리의 편리함: 유지보수 프로그래머를 위한 설계
  • 느슨한 결합: 프로그램의 서로 다른 부분들 간의 연결을 최소화하도록 설계하는 것을 의미한다.
  • 클래스 간의 연결이 최대한 적도록 설계하기 위하여 클래스 인터페이스에서의 추상화와 캡슐화, 정보의 은닉과 같은 방법을 사용.
  • 연결이 최소화되면 통합이나 테스트, 유지 보수 시에도 작업을 줄일 수 있다.
  • 확장성: 예측가능한 변경
  • 재사용성: 현재 시스템의 일부를 다른 시스템에서도 사용할 수 있도록 시스템을 설계하는 것
  • 높은 팬인(fan-in): 주어진 클래스를 사용하는 클래스의 수가 많다는 것을 의미
  • 낮은 팬아웃(fan-out): 주어진 클래스가 다른 클래스를 적게 사용한다는 것을 의미
  • 이식성: 시스템을 다른 환경으로 쉽게 이동시킬 수 있도록 설계하는 것을 의미
  • 간결성: 추가적인 부분이 없도록 시스템을 설계하는 것을 의미
  • 계층화: 시스템을 특정한 수준으로 살펴보고 일관된 뷰를 얻을 수 있도록 분산 계층을 유지하는 것을 의미 한다.
  • 표준 기술들: 표준화되고 일반적 접근 방법을 사용.

5.2.3. 설계 수준

  • 1수준(소프트웨어 시스템): 전체 시스템
  • 2수준(서브시스템이나 패키지로 분할)
    • 프로그램을 주요 서브시스템으로 어떻게 분할할 것인지와 각 서브시스템이 다른 서브시스템 어떻게 사용할지를 결정하는 일이다.
    • 서브시스템간의 커뮤니케이션은 “알아야 할 필요성”이 있을때에만 가능하도록 한다.
    • 연결을 이해하고 쉽게 유지하기 위해서 내부 서브시스템의 관계를 간단하게 만들도록 한다.
    • 시스템 수준의 다이어그램은 비순환 그래프이어야 한다.
    • 공통적인 서브시스템
      • 비즈니스 규칙
      • 사용자 인터페이스
      • 데이터베이스 접근
      • 시스템 의존성 : 이식성 상승한다.
  • 3수준(클래스로 분할) 이 수준에서의 설계는 시스템에 있는 모든 클래스들을 규명한다.
    • 클래스와 객체: 객체와 클래스간의 차이점 클래스는 프로그램 코드에서 보게 되는 정적인 것이고, 객체는 프로그램이 실행했을 때 보게되는 특정한 값과 특성을 갖는 동작이다.
  • 4수준(루틴으로 분할): 클래스의 private 루틴들을 상세히 설계

  • 5수준(내부 루틴 설계): 개별적인 루틴의 상세한 기능을 구현
  • 설계는 의사코드(pseudo code)를 작성하고, 참고 서적에서 알고리즘을 살펴보고, 루틴 내의 코드 단락을 어떻게 구성할것인지를 결정하고, 프로그래밍 언어로 코드를 작성하는 활동들로 구성.

  • 의사코드: 일반적인 언어로 코드를 흉내내어 알고리즘을 써놓은 코드를 말한다. 특정언어로 프로그램을 작성하기 전에 알고리즘 모델을 대략적으로 모델링하는데 쓰인다.

5.3. 빌딩 블록의 설계: 발견적 학습(객체지향 설계 관점)
설계는 결정적이지 않기 때문에, 효율적인 경험들에 대한 훌륭한 응용 분야가 훌륭한 소프웨어 설계에서의 핵심적인 활동이다. => 먼말이여 …?

5.3.1. 실세계의 객체를 찾아라.
“처음부터 시스템이 무엇을 하는지 묻지 말고 무엇을 해야 하는지 요구하라!”

  • 객체설계 과정
  • 객체의 특성(메서드와 데이터)를 규명한다.
  • 각 객체가 무엇을 할 수 있는지 결정한다.
  • 각 객체가 다른 객체에게 무엇을 허용하는지 결정한다. 객체가 서로에게 할 수 있는 일반적인 두 가지 사항은 포함과 상속이다.
  • 각 객체에서 다른 객체에 보여질 부분을 결정한다. 즉, public인 부분과 private인 부분을 결정한다. (정보은닉)
  • 각 객체의 public 인터페이스를 정의한다.

5.3.2. 일관성 있는 추상화를 구성하라. 추상화를 이용하면 복잡한 개념을 보다 단순한 관점으로 바라볼 수 있다.!!!!

5.3.3. 세부 사항들을 캡슐화하라 추상화는 객체를 높은 수준에서 볼 수 있도록 하고, 캡슐화는 다른 수준에서는 객체를 볼 수 없도록 한다. 캡슐화는 복잡한 부분을 보지 못하게 함으로써 복잡성을 관리하는데 도움을 준다.

5.3.4. 상속이 설계를 단순화할 수 있을 때 상속하라. 객체들 간의 유사성과 차이점을 정의하는 것을 상속이라 부른다. 상속은 프로그램을 단순화 시킨다.

5.3.5. 비밀을 숨겨라(정보은닉) 객체지향 설계에서 정보 은닉이 캡슐화와 모듈화를 발생케 하였으며, 추상화 개념과 연결된다. 정보 은닉은 복잡성을 감추는데 중점을 두고 있다.

  • 비밀과 비밀 보장권 변할 것 같은 영역이나 파일의 포맷, 데이터 타입이 구현되는 방법, 또는 해당 영역에 있는 오류가 가능한 한 프로그램에 입히는 피해가 적도록 프로그램의 다른 부분으로부터 차단될 필요가 있는 영역들이 비밀이 된다. “완전하고 최소한의 클래스 인터페이스를 작성하기 위해 노력하라!!”

  • 비밀의 두 종류
  • 특별하게 관심이 없는 경우에 머리 속에 다룰 필요가 없도록 복잡성을 감추는 것
  • 변경이 발생했을 때, 그 효과가 지역화되도록 변경의 원인을 감추는 것

  • 정보 은닉에 대한 장애물 정보의 지나친 배분 순환 참조 클래 데이터를 전역 데이터로 혼동 성능 손해

  • 정보 은닉의 가치 무엇을 숨겨야 하는지에 대한 질문은 모든 수준에서 좋은 설계 결정에 도움을 준다.

5.3.6. 변경될 거 같은 영역을 규명하라.
5.3.6.1. 변경될 것처럼 보이는 항목을 규명한다.
5.3.6.2. 병경될 것 같은 항목을 분류한다.
5.3.6.3. 변경될 것처럼 보이는 항목을 고립시킨다.

-변경될거같은 영역

  • 비즈니스 규칙
  • 하드웨어 의존성
  • 입력과 출력
  • 비표준 언어 기능
  • 어려운 설계와 구현 부분
  • 상태변수 불린 변수를 상태 변수처럼 사용하지 말라. 대신 열거 형을 사용하라. 상태 변수에 새로운 상태를 추가하는 일은 흔히 발생하며, 열거 형에 새로운 타입을 추가하면 변수를 검사하는 모든 코드를 검토하는 대신 컴파일만 다시 하면 된다. 변수를 직접적으로 접근하는 대신 접근 루틴을 사용하라. 변수 대신 접근 루틴을 검사함으로써, 보다 정교하게 상태를 검사할 수 있다.
  • 데이터 크기 제약

-변경의 정도 예상하기

5.3.7. 느슨한 결합을 유지하라(loose coupling) 결합(coupling)은 클래스나 루틴이 다른 클래스나 루틴과 얼마나 밀접하게 연관되어 있는지를 기술한다. 모듈간의 연결은 단순하게 만들어야 한다.

  • 결합의 기준
  • 크기: 모듈간의 연결의 수를 의미한다.

  • 가시성: 두 모듈 간 연결의 현저함을 의미한다. 매개변수 목록에 데이터를 전달하는 것은 분명한 연결을 하는 것이기 때문에 좋지만, 다른 모듈이 데이터를 사용할 수 있도록 전역 데이터를 수정하는 것은 교묘한 연결이기 때문에 나쁘다. 전역 데이터 연결을 문서화하는 것은 그러한 연결을 분명하게 만들기 때문에 약간 낫다.

  • 유연성:모듈 간의 연결을 얼마나 쉽게 변경할 수 있는지를 의미한다. 다른모듈이 어떤 모듈을 쉽게 호출할수록, 결합이 느슨해지며 보다 유연하고 유지 보수가 가능해지기 때문에 더 좋다. 시스템 구조를 생성할 때, 최소한의 상호 연결을 기준으로 프로그램을 나누어라. 만약 프로그램이 나무 한그루라면, 깨알같이 쪼개도록 한다.

  • 결함의 종류

    • 간단한-데이터-매개변수 결합: 만약 두 모듈 사이에서 전달되는 모든 데이터가 기본 데이터 형이고 모든 데이터가 매개변수 목록을 통해서 전달된다면, 두 모듈은 간단한-데이터-매개변수로 결합되어 있다. 이러한 종류의 결합은 자연스럽고 허용 가능하다.

    • 간단한-객체 결합: 만약 모듈이 객체를 인스턴스화한다면, 그 모듈은 객체에 간단한-객체로 결합되어 있다. 이러한 종류의 결합은 훌륭하다.

    • 객체-매개변수 결합: 만약 Object1이 Object2에게 Object3을 자신에게 넘겨주도록 요구한다면, 두 모듈은 서로에 대해서 객체-매개변수로 결합되어 있다. 이러한 종류의 결합은 Oject2가 Object3에 대해서 알아야 하기 때문에, Object1이 Object2에게 기본 데이터 형을 자신에게 넘기도록 하는 것보다 결합이 좀 더 강하다.

    • 의미론적 결합: 한 모듈이 다른 모듈의 구문론적인 요소로 이용하는 것이 아니라 다른 모듈의 내부 작동에 대한 의미론적인 지식을 사용하고 있을 때, 가장 발견하기 어려운 종류의 결합이 발생한다.

KeyPoint: 클래스와 루틴은 복잡성을 줄이기 위해서 제일 먼저 사용되어야 하는 지능적인 도구이다.

5.3.8. 일반적으로 널리 사용되는 설계
설계 패턴은 소프트웨어에서 가장 흔히 발생하는 많은 문제들을 해결하기 위해 사용될 수 있는 이미 만들어진(ready-made) 해결책의 핵심적인 부분을 제공한다.

  • 패턴은 이미 만들어진 추상화를 제공함으로써 복잡성을 줄인다.
  • 패턴은 일반적으로 널리 사용되는 해결책의 세부 사항등을 규정함으로써 오류를 줄인다.
  • 패턴은 설계 대안들을 제안함으로써 발견적 학습의 가치를 제공한다.
  • 패턴은 설계 대화를 보다 높은 수준으로 옮김으로써 의사 소통을 원활하게 한다.

  • 주의사항
    • 패턴을 사용함에 있어서 주의해야 할 부분은, 패턴을 사용하기 위해 코드를 억지로 끼워 맞추는 것이다. 어떤 경우에는 잘 알려진 패턴을 따르기 위해서 코드를 변형하면 코드에 대한 이해를 향상시킬 수 있다. 하지만 코드가 지나치게 변형되어야 한다면, 표준 패턴처럼 보이도록 하려는 작업이 복잡성을증가시킬 수도 있다.
    • 패턴이 적절한 설계 방법이라기보다는 패턴을 사용해 보기 위해서 패턴을 사용하는 것이다.

5.8.9. 다른 발견적 학습 (어떤 생각을 갖고 개발할것인가?)

  • 강한 응집력을 목표로 세워라. (느슨한 결합 강한응집력!!!)
  • 계층을 만들어라.(정보의 계층적 구조화: 추상화- > 상세화) 계층은 개념의 가장 일반적인 또는 추상적인 항목이 계층의 최상위에 포함되고, 점차적으로 상세하고 구체적인 항목이 낮은 수준의 계층에 포함되는 층으로 이루어진 정보 구조이다.
  • 클래스 계약을 형식화하라.
  • 책임을 할당하라. 객체에 어떻게 책임을 할당할 것인가를 생각
  • 테스트를 위해서 설계하라.
  • 실패를 피하라. 이전의 성공에 촛점을 맞추기보다는 실패를 피하는 것에 염두를 두고 설계하라.
  • 바인딩 시간을 의식적으로 선택하라. 바인딩 시간은 특정한 값이 변수에 결합되는 시간이다.
  • 제어의 중심점을 만들어라. 정확한 장소의 법칙-중요한 코드를 찾기 위한 정확한 장소와 유지 보수 변경을 수행하기 위한 정확한 장소가 있어야 한다.
  • 주먹구구식 기법의 사용을 고려하라. 의심스러울 때에는 주먹구구식 방법을 사용한다.
  • 다이어그램을 그려라. 그림을 그려라.
  • 설계의 모듈화를 유지하라. 모듈화의 목표는 각 루틴이나 클래스를 “블랙박스”처럼 만드는 것이다.

설계의 발견적 학습에 대한 요약

  • 실세계의 객체를 찾아라.
  • 일관성 있는 추상화를 구성하라.
  • 세부 사항들을 캡슐화하라.
  • 상속이 설계를 단순화할 수 있을 때 상속하라.
  • 비밀을 숨겨라(정보은닉)
  • 변경될 것 같은 영역을 규명하라.
  • 느슨한 결합을 유지하라.
  • 일반적으로 널리 사용되는 설계 패턴을 찾아라.

다음은 때때로 유용한 발견적 학습들

  • 강한 응집력을 목표로 세워라.
  • 계층을 만들어라.
  • 클래스 계약을 형식화하라.
  • 책임을 할당하라.
  • 테스트를 위해서 설계하라
  • 실패를 피하라
  • 바인딩 시간을 의식적으로 선택하라
  • 제어의 중심점을 만들어라
  • 주먹구구식 기법의 사용을 고려하라
  • 다이어그램을 그려라
  • 설계의 모듈화를 유지하라

5.3.9. 발견적 접근을 위한 지침

  1. 문제의 이해
  2. 계획의 고안
  3. 계획의 실행
  4. 검토

5.4. 설계 방법

  • 반복: 설계는 반복적인 프로세스이다. 언제나 A의 위치에서 B의 위치로만 가지는 않는다. 때로는 A에서 B로 가고, 다시 A로 돌아오기도 한다.

  • 분할 정복: 프로그램을 서로 다른 관심의 영역으로 나누고, 각 부분을 개별적으로 처리하도록 한다. 만약 어떤 영역에서 막다른 길을 만난다면, 반복하도록 한다. 점증적인 개선은 복잡성을 다루는 강력한 도구이다.

  • 하향식(Top down)과 상향식(Botton up) 설계 접근 방법 둘다 해보는것이 좋다.
    • 하향식 설계는 높은 추상화 수준에서 시작한다. 기본 클래스나 구체적이지 않은 다른 설계 요소들을 정의한다. 설계를 개발함에 따라, 파생 클래스를 규명하고, 클래스들을 협력하고, 다른 세부적인 설계 요소들로 상세화 수준을 증가시킨다. => 분해전략
    • 상향식 설계는 구체적인 것부터 시작해서 일반적인 쪽으로 작업을 한다. 전형적으로 구체적인 객체들을 규명하는 것부터 시작해서 객체들의 집합을 일반화시키고 기본 클래스를 생성한다. => 결합전략
  • 실험적인 프로토타이핑(prototyping)

  • 협력적인 설계

  • 설계를 얼마나 해야 할까?
    요소 | 구현하기 전에 설계에서 필요한 상세 수준 | 문서화 형식

  • 설계 작업 기록하기 * 설계 문서를 코드 자체에 넣어라. (pseudo code 작성 후 주석 처리)
    • 설계에 대한 논의와 결정을 위키(Wiki)에 기록하라.
    • 이메일(e-mail)로 요약을 작성하라
    • 디지털 카메라를 사용하라.
    • 설계 플립 차트를 보관하라.
    • CRC(클래스, 책입 , 협력자)카드를 사용하라
    • 적절한 상세 수준에서 UML 다이어그램을 작성하라. UML은 표준화되었기 때문에, 설계 개념을 의사 소통하는 데 있어서 공통적인 인식을 지원하고 , 그룹 단위로 작업할 때 설계 대안들을 고려하는 작업을 빠르게 진행 할 수 있게 한다.

[설계 방법을 적용하는 데 있어서 독단적일수록, 해결할 수 있는 문제는 적어진다. 설계를 다루기 힘들고, 부주의하고, 발견적 학습 프로세스로 취급하라. 첫 번째 설계로 정하지 말라. 협력하라. 단순함을 추구하라. 필요할 때에는 프로토타이핑하라. 반복하고, 반복하고, 또 반복하라. 그러면 설계에 만족할 수 있을 것이다.]

요점 정리

  • 소프트웨어의 일차적인 기술적 의무는 복잡성을 관리하는 것이다. 이것은 단순함에 촛점을 맞춘 설계의 도움을 크게 받을 수 있다.
  • 단순함은 두 가지의 일반적인 방법으로 달성할 수 있다. 누군가가 한번은 처리해야 하는 본질적인 복잡성의 양을 최소화하고 부수적인 복잡성이 불필요하게 증가하지 않도록 하는 것이다.
  • 설계는 발견적인 학습이다. 어떠한 단일 방법론에 독단적으로 집착하면 상상력과 프로그램에 해를 입힌다.
  • 좋은 설계는 반복이다. 여러 번 시도할수록, 마지막 설계는 더 좋아 질 것이다.
  • 정보 은닉은 매우 유용한 개념이다. “무엇을 숨겨야 하는가?”라는 질문이 해결하기 어려운 설계 상의 문제들을 해결해 준다. | Style : Background0, Font0, Size16 |

Toby 스프링3 2부 1장 IoC 컨테이너와 DI

|

1장 IoC 컨테이너와 DI

1.1 IoC 컨테이너 : 빈 택토리와 애플리케이션 컨텍스트

  • IoC 컨테이너 : 컨테이너가 코드 대신 오브젝트의 제어권을 가지고 있다.
  • 빈 팩토리 : 오브젝트의 생성과 오브젝트 사이의 런타임 관계를 설정하는 DI 관점으로 볼때는 컨테이너를 빈 팩토리가 된다.
  • 스프링 컨테이너 또는 IoC 컨테이너라고 말하는 것은 바로 ApplicationContext 인터페이스 구현한 클래스 오브젝트이다.
1.1.1 IoC 컨테이너를 이용해 애플리케이션 만들기

POJO 클래스와 클래스 간의 관계를 정의한 메타 정보를 가지고 스프링 컨테이너로 빈을 생성하게 하면 된다.
스프링 IoC컨테이너는 각 빈에 대한 정보를 담은 설정 메타정보를 읽어들인 뒤에, 이를 참고해서 빈오브젝트를 생성하고 프로퍼티나 생성자를 통해 오브젝트를 주입해주는 DI 작업을 수행한다.

POJO 클래스(Plain Old Java Object)
설정 메타정보

  • BeanDefinition
    • 빈 아이디, 이름, 별칭
    • 클래스 또는 클래스 이름
    • 스코프 : 싱글톤, 프로토타입과 같은 빈의 생성방식과 존재 범위
    • 프로퍼티 값 또는 참조: DI에 사용할 프로퍼티 이름과 값 또는 참조할 빈의 이름
    • 지연된 로딩의 여부, 우선 빈 여부, 자동와이어링 여부, 부모 빈 정보, 빈 팩토리 이름 등
  • BeanDefinitionReader : 메타정보를 정의한 원본(xml, java등등)의 포맷과 구조, 자료의 특성에 맞게 읽어와 BeanDefinition 오브젝트로 변환해 준다.

1.2. IoC 컨테이너 종류와 사용 방법

StaticApplicationContext
- 코드를 통해 빈 메타정보를 등록하기 위해 사용    
- 스프링 기능에 대한 학습 테스트에 사용
- 웹관련 기능 학습 => StaticWebApplicationContext를 사용    
GenericApplicationContext
- 가장 일반적인 애플리케이션 컨텍스트의 구현 클래스
GenericXmlApplicationContext
- GenericApplicationContext + XmlBeanDefinitionReader    
WebApplicationContext
보통 main과 같은 기동 역할을 맡은 빈을 사용하려면 IoC 컨테이너에 요청해서 빈 오브젝트를 가져와야 한다. IoC 컨테이너의 역할은 이렇게 초기에 빈 오브젝트를 생성하고 DI한 후에 최초로 애플리케이션을 기동할 빈 하나를 제공해주는 것이다.    

하지만, 웹애플리케이션을 동작하는 방식이 근복적으로 다르다. 독립 자바 프로그램은 자바 VM에게 main()메소드를 시작 시켜달라고 할수 있다. 하지만 웹에서는 main()을 호출할 방법이 없다.    

그래서 웹환경에서는 main() 메소드 대신 서블릿 컨테이너가 브라우저로부터 오는 HTTP 요청을 받아서 해당 요청에 매핑이 되어 있는 서블릿을 실행해주는 방식으로 동작 한다. 서블릿이 일종의 main() 메소드와 같은 역할을 한다.    

스프링은 웹 환경에서 애플리케이션 컨텍스트를 생성하고 설정 메타정보로 초기화 해주고, 클라이언트로부터 들어오는 요청마다 적절한 빈을 찾아서 이를 실행해주는 DispatcherServlet이라는 이름의 서블릿을 제공한다. 스프링이 제공해준 서블릿을 web.xml에 등록하는 것으로 웹환경에서 스프링컨테이너가 만들어지고 애플리케이션을 실행하는 데 필요한 대부분의 준비가 끝난다.

web.xml 파일은 웹애플리케이션의 배포 설명자이다.

웹애플리케이션의 컨텍스트 구성 방법

  • 서블릿 컨텍스트와 루트 애플리케이션 컨텍스트 계층구조
    가장 많이 사용되는 기본구성 방법
    스프링 웹기술을 사용하는 경우 웹관련 빈은 서블릿컨텍스트에 두고 나머지는 루트 애플리케이션 컨텍스트에 등록한다.
    루트 컨텍스트는 모든 서블릿 레벨 컨텍스트의 부모 컨텍스트가 된다.

  • 루트 애플리케이션 컨텍스트 단일 구조
    스프링 웹기술을 사용하지 않고 서드파티 웹 프레임워크나 서비스엔진만을 사용해서 프리젠테이션 계층을 만든다면 스프링 서블릿을 둘 필요가 없다. 이때는 루트 컨텍스트만 등록한다.

  • 서블릿 컨텍스트 단일구조
    스프링 웹기술을 사용하면서 스프링외의 프레임워크나 서비스 엔진에서 스프링 빈을 이용할 생각이 아니라면 루트 애플리케이션 컨텍스트를 생략할 수 있다.
    이때 서블릿 안에 만들어지는 애클리케이션 컨텍스트는 부모 컨텍스트를 갖지 않기 때문에 스스로 루트 컨텍스트가 된다. 하지만 웹 애플리케이션 레벨에 두는 공유가능한 루트컨텍스트와는 구분이 된다.

루트 애플리케이션 컨텍스트 등록

    <listener>    
      <listener-class>org.springframework.web.context.ContextLoaderListener    
      </listener-class>    
    </listener>

ServletContextListener 인터페이스: 웹 애플리케이션의 시작과 종료 시 발생하는 이벤트를 처리하는 리스너
웹 애플리케이션의 web.xml 파일 안에 리스너 선언을 등록

ContextLoaderListener: 웹애플리케이션이 시작할 때 자동으로 루트 애플리케이션 컨텍스트를 만들고 초기화 해준다.
Default 설정
- 애플리케이션 컨텍스트 클래스 : XmlWebApplicationContext
- XML설정파일 위치 : /WEB-INF/applicationContext.xml

  • contextConfigLocation : XML설정파일 위치 지정
<context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>
        /WEB-INF/daoContext
        /WEB-INF/applicationContext.xml
      </param-value>
</context-param>
  • contextClass : 컨택스트 클래스 지정
<context-param>
  <param-name>contextClass</param-name>
  <param-value>
    ort.springframework.web.context.support.AnnotationConfigApplicationContext
  </param-value>
</context-param>

AnnotationConfigApplicationContext를 사용할 때는 contextConfigLocation를 반드시 선언해 주어야 한다.

서블릿 애플리케이션 컨텍스트 등록

스프링의 웹 기능을 지원하는 프론트 컨트롤러 서블릿은 DispatcherServlet이다.
서블릿의 이름을 다르게 지정하면 하나의 웹 애플리케이션에 여려개의 DispatcherServlet을 등록할 수 있다. 각 DispatcherServlet은 서블릿이 초기화 될 때 자신만의 컨텍스트를 생성하고 초기화 한다. 동시에 웹 애플리케이션 레벨에 등록된 루트 애플리케이션 컨텍스트를 찾아서 이를 자신의 부모 컨텍스트로 사용 한다.
서블릿 컨텍스트를 위한 서블릿 등록

<servlet>
  <servlet-name>spring</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet
  </servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>
  • <servlet-name>
    DispatcherServlet에 의해 만들어지는 애플리케이션 컨텍스트는 모두 독립적인 네임스페이스 를 만든다. 이 네임스페이스는 서블릿 단위로 만들어지는 컨텍스트를 구분하는 키가 된다. 네임스페이스는 서블릿 이름 + servlet이 된다.
    네임스페이스가 중요한 이유는 DispatcherServlet이 사용할 디폴트 xml 설정파일의 위치를 네임스페이스를 이용해서 만들기 때문이다.
    서블릿 컨텍스트가 사용할 디폴트 설정파일은 아래 규칙으로 만들어진다.
      '/WEB-INF'+서블릿네임스페이스+'.xml'   
    

    네임스페이스를 만드는 이유는 여러 개의 DispatcherServlet이 등록되더라도 각각 구분할 수 있고, 자신만의 디폴트 설정파일을 가질 수 있도록 하기 위해서이다.

  • <load-on-startup>
    load-on-startup은 서블릿 컨테이너가 등록한 서블릿을 언제 만들고 초기화할 지, 또 그 순서는 어떻게 되는지를 지정하는 정수 값이다. 이 항목을 생략하거나 음의 정수로 넣으면 해당 서블릿은 서블릿 컨테이너가 임의로 정한 시점에서 만들어지고 초기화된다. 반대로 0 이상의 값을 넣으면 웹 애플리케이션이 시작되는 시점에서 서블릿을 로딩하고 초기화한다. 또한 여러 개의 서블릿이 등록되어 있다면 작은 수를 가진 서블릿을 우선적으로 만들어 진다.
    DispatcherServlet은 서블릿의 초기화 작업 중에 스프링 컨텍스트를 생성한다. 컨텍스트의 설정이나 환경에 문제가 있다면 컨텍스트 생성 시 대부분의 확인이 가능하다. 따라서 웹 애플리케이션이 시작되고 가능한 한 빨리 서블릿 컨텍스트의 초기화가 진행되는 것이 바람직하다. 그래야 문제를 빠르게 파악할 수 있다.