2017-12-18_TIL
18 Dec 2017 | intellij2017년 12월 18일.
IntelliJ 사용 시 직접 웹브라우저를 실행하지 말고 왼쪽 작업 디렉토리에서 해당 페이지를 우클릭해서 열면 페이지 변경 시 자동 반영된 것이 보인다.
IntelliJ 사용 시 직접 웹브라우저를 실행하지 말고 왼쪽 작업 디렉토리에서 해당 페이지를 우클릭해서 열면 페이지 변경 시 자동 반영된 것이 보인다.
프로그래머의 길, 멘토에게 묻다.
프로그래머로서 주요 업무를 운영/개발로 맡아 왔지만, 근 몇년간 업무로 개발을 하지 않았다.
어떻게 앞으로 해야할 지 궁금하던 차에 알게되어 읽기 시작하였다.
숙련된 프로그래머를 위한 책이 아닌 이제 막 이 분야에 진입하거나 아직 프로그래밍 실력이 부족한 이에게 방향을 잡아주는 데 목적을 두고 있는 책이다.
Resource : 명사형으로 표현한다
Message : http body 담아서 보낸다
Method : http method
POST : CREATE
GET : SELECT (Idempotent)
PUT : UPDATE (Idempotent)
DELETE : DELETE (Idempotent)
{servicename}/{version}/{REST URL}
외부에서는 api url로 통해 들어오고 reverse proxy를 사용하여 URL 매핑을 사용 한다. 외부 노출된 URL변경이 없이 향후 확장되었을 때 서버를 물리적으로 분리해내기 편리하다.
/terry/friends?field=id,name
권한 인가
API 인가 방식
- RBAC(Role Based Access Control) : Role로 권한을 묶고 role을 부여하는 방식
- ACL(Access Control List) : 직접 권한 부여 방식
API 권한 인가 처리 위치
- 클라이언트에 의한 API 권한 인가 처리
- 게이트웨이에 의한 권한 인가 처리
- 서버에 의한 API 권한 인가 처리 : 가장 일반적이고 보편적인 인가 방식
네트워크(전송) 레벨 암호화
- Man in the middle attack : 해커는 인증기관의 인증서를 사용 할 수 없으므로 반드시 서버의 인증서를 확인 하도록 한다.
메시지 본문 암호화
- RSA 알고리즘
메시지 무결성 보장
- HMAC에 timestamp를 사용 하여 무결성 및 재사용 공격을 방어한다.
자바스크립트 클라이언트 지원
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은 해당 세션에서만 유효하도록 한다.
내용
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. 바람직한 설계의 특징
5.2.3. 설계 수준
4수준(루틴으로 분할): 클래스의 private 루틴들을 상세히 설계
설계는 의사코드(pseudo code)를 작성하고, 참고 서적에서 알고리즘을 살펴보고, 루틴 내의 코드 단락을 어떻게 구성할것인지를 결정하고, 프로그래밍 언어로 코드를 작성하는 활동들로 구성.
5.3. 빌딩 블록의 설계: 발견적 학습(객체지향 설계 관점)
설계는 결정적이지 않기 때문에, 효율적인 경험들에 대한 훌륭한 응용 분야가 훌륭한 소프웨어 설계에서의 핵심적인 활동이다.
=> 먼말이여 …?
5.3.1. 실세계의 객체를 찾아라.
“처음부터 시스템이 무엇을 하는지 묻지 말고 무엇을 해야 하는지 요구하라!”
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. 발견적 접근을 위한 지침
5.4. 설계 방법
반복: 설계는 반복적인 프로세스이다. 언제나 A의 위치에서 B의 위치로만 가지는 않는다. 때로는 A에서 B로 가고, 다시 A로 돌아오기도 한다.
분할 정복: 프로그램을 서로 다른 관심의 영역으로 나누고, 각 부분을 개별적으로 처리하도록 한다. 만약 어떤 영역에서 막다른 길을 만난다면, 반복하도록 한다. 점증적인 개선은 복잡성을 다루는 강력한 도구이다.
실험적인 프로토타이핑(prototyping)
협력적인 설계
설계를 얼마나 해야 할까?
요소 | 구현하기 전에 설계에서 필요한 상세 수준 | 문서화 형식
[설계 방법을 적용하는 데 있어서 독단적일수록, 해결할 수 있는 문제는 적어진다. 설계를 다루기 힘들고, 부주의하고, 발견적 학습 프로세스로 취급하라. 첫 번째 설계로 정하지 말라. 협력하라. 단순함을 추구하라. 필요할 때에는 프로토타이핑하라. 반복하고, 반복하고, 또 반복하라. 그러면 설계에 만족할 수 있을 것이다.]
요점 정리
POJO 클래스와 클래스 간의 관계를 정의한 메타 정보를 가지고 스프링 컨테이너로 빈을 생성하게 하면 된다.
스프링 IoC컨테이너는 각 빈에 대한 정보를 담은 설정 메타정보를 읽어들인 뒤에, 이를 참고해서 빈오브젝트를 생성하고 프로퍼티나 생성자를 통해 오브젝트를 주입해주는 DI 작업을 수행한다.
POJO 클래스(Plain Old Java Object)
설정 메타정보
- 코드를 통해 빈 메타정보를 등록하기 위해 사용
- 스프링 기능에 대한 학습 테스트에 사용
- 웹관련 기능 학습 => StaticWebApplicationContext를 사용
- 가장 일반적인 애플리케이션 컨텍스트의 구현 클래스
- GenericApplicationContext + XmlBeanDefinitionReader
보통 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
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/daoContext
/WEB-INF/applicationContext.xml
</param-value>
</context-param>
<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>
'/WEB-INF'+서블릿네임스페이스+'.xml'
네임스페이스를 만드는 이유는 여러 개의 DispatcherServlet이 등록되더라도 각각 구분할 수 있고, 자신만의 디폴트 설정파일을 가질 수 있도록 하기 위해서이다.