SSL 발급받기

SSL 인증서를 구입하기 위해 저렴한 사이트들이 많다. 나는 GOGETSSL에서 인증서를 구입했다.

SSL 인증서 발급받기 위한 자세한 사항은 아래의 링크에서 참고 하기 바란다.

GOGETSSL에서 인증서 발급받기

요점을 정리하면, 인증서 등록시 CSR Generate 를 클릭하게 되면 CSR Code와 Server private key가 생성이 된다.

private key에 필요한 정보를(적용할 도메인 url) 입력한 다음 단계를 진행하다 보면 도메인 소유 인증을 진행한다. 각자 도메인을 구입한 사이트에서 E-mail, DNS, HTTP, HTTPS 기반의 인증방식을 제공하니 메뉴얼대로 진행하면 된다.

(주의해야할 점은 기존의 private key는 한번 잃어버리면 다시 재발급이 안된다. CSR/private Key pair을 다시 생성해야 한다.)

나는 DNS 인증방식으로 진행하였고 제공된 CNAME 코드로 DNS 서버의 CNAME Recorde를 등록하였다.

입력한 인증 정보를 통해 인증 진행후 인증서 파일을 다운로드를 받을 수 있다.

Nginx에 적용하기

/etc/nginx/site-available 폴더에서 server block 이 설정된 파일을 들어가보자.

기본으로 아래와 같이 되어 있다. 아래의 코드의 의미를 살펴 보면 80번 포트로(HTTP프로토콜의 기본 포트) example.com 도메인으로 요청이 들어오면 해당하는 location에 명시된 행위를 하겠다는 것을 의미한다.

server {
  listen 80;
  server_name example.com;

  location / {

  }
}

우리는 SSL을 적용해야 하기 때문에 HTTPS의 기본포트인 443 포트를 적용해야 한다.

server {
  listen 443;
}

그리고 SSL 인증서 발급시 생성된 key 파일과 crt 파일을 서버에 위치시키고 다음과 같이 작성한다.

server {
  listen 443;
  server_name example.com
  root html;

  ssl       on;
  ssl_certificate /etc/nginx/ssl/my_domain.crt;
  ssl_certificate_key /etc/nginx/ssl/my_domain.key;
  ssl_prefer_server_ciphers on;
  ssl_session_timeout 5m;

  location / {

  }
}

보안을 위해 nginx에 ssl폴더에 넣었지만 보안을 위해 루트 권한 설정을 한다.

$ sudo chown -R root:root /etc/nginx/ssl;

이제 https://example.com 으로 접속하게 되면 접속이 되는 것을 확인할 수 있다.

Chain Error Issue

일반 최신 웹 브라우저에서 접속하면 정상적으로 인증이 통과되어 접속이 된다. 하지만 구형 웹브라우저나 일부 모바일기기의 웹뷰에서는 접속이 안되는 경우가 있다. 이를 위해 먼저 체인 인증서에 대해 알아보자

체인 인증서란?

루트 인증서와 발급 받은 서버 인증서 사이에 존재하는 인증서이다. 즉 루트 인증서와 서버 인증서 사이를 중개, 연결해준다는 것이다. SSL 동작 방식에 의거하여 웹 브라우저에서 CA라는 Root Certificate 에서 통과된 인증서인지를 확인 후 접속을 허락해준다.

근데 이 체인 인증서라는게 SSL 초장기에는 루트 인증서에서 바로 서버 인증서를 발급하였지만, 최신에는 기술 환경 변화에 맞게 체인인증서 발급 체계를 가지고 있다.

따라서 최신 웹 브라우저의 경우에는 최신 루트 인증서 뿐만 아니라 체인 인증서까지 보유하고 있지만, 구형 웹 브라우저나 일부 모바일 기기에서는 접속이 안되는게 이러한 이유가 존재한다.

체인 이슈 해결

서버쪽에서 명시적으로 현재 서버인증자가 누구이며 어떤 발급 단계를 통하였는지 알려주면 되는 것이다.



위 사진에서 전체 zip 파일을 다운 받는다. 그러면 내부에 3가지의 파일이 존재할 것이다.

my_domain.crt, AddTrust_External_CA_Root.crt, COMODO_RSA_Certification_Authority.crt

nginx CA 인증서를 명시할 지시자 없으므로 다음과 같이 SSL 인증서와 CA 인증서를 하나의 파일로 만들어야 한다.

$ cat my_domain.crt AddTrust_External_CA_Root.crt COMODO_RSA_Certification_Authority.crt > ssl-bundle

그리고 가끔씩 순서를 잘 못 맞춰서 key와 crt파일이 매칭이 되지 않는 경우가 있는데 이 때는 sslshopper 에서 매칭 여부를 확인할 수 있다.

이제 SSL 인증서와 CA 인증서를 하나로 합친 파일을 nginx에 등록시키자.

server {
  listen 443;
  server_name example.com
  root html;

  ssl       on;
  ssl_certificate /etc/nginx/ssl/ssl-bundle.crt;
  ssl_certificate_key /etc/nginx/ssl/my_domain.key;
  ssl_prefer_server_ciphers on;
  ssl_session_timeout 5m;

  location / {

  }
}

자, 그러면 체인 문제가 해결되었는지 sslshopper에서 확인해보자.

그림처럼 오른쪽에 Server hostname을 입력하고 확인해 보 수 있다.

'IT > BackEnd' 카테고리의 다른 글

Mysql UDF 작성  (0) 2019.03.20
Crawling을 위한 Selenium, Chrome Driver 설치  (0) 2018.07.15
Atom Remote-FTP package 소개  (0) 2017.11.02
JWT  (0) 2017.09.27

서브 도큐먼트 검색 후 값 변경 시키기

다음과 같이 서브 스키마가 정의되어 있는 부모 스키마가 있다고 가정해보자.

A = {
  B = [
    new Schema({
      content: String
    })
  ]
}

다음과 같은 query로 B의 unique id를 가진 부모 도큐먼트를 검색할 수 있다.

A.findOneAndUpdate({"B._id": sub_uid})

만약, 찾은 후에 서브 도큐먼트의 값을 변경시키고 싶다면 어떻게 해야할까? 방법이야 많겠지만 내가 찾은 방법은 아래의 방법이다.

A.findOneAndUpdate({"B._id": sub_uid}, {$set: {"B.$.content": content}})

정리

다음의 .$. 을 이용하여 $set 옵션 사용시 해당 필드의 값을 변경할 수 있다. 서브 도큐먼트 없이 부모 도큐먼트 하나로 값을 관리할 수 있다면 좋겠지만, 가끔씩 서브 도큐먼트를 사용해야 할 경우가 생기면 위와 같은 방법을 사용해면 좋겠다.

'IT > Mongodb' 카테고리의 다른 글

도큐먼트 필드 삭제하기  (0) 2017.12.30
$elemMatch vs $in  (0) 2017.10.31
ISO Date 값이 한국 시간과 다를때  (1) 2017.10.31
스키마 모델링 어떻게 해야 하나  (0) 2017.10.21

error handler 와 router, middleware의 비교

node js 에서는 기본적으로 error handler 를 제공한다.

app.use((err, req, res, next) => {
 
})

그리고 err 인자가 없는 경우 미들웨어로 간주된다.

app.use((req, res, next) => {
 
})

그리고 아래의 경우에는 라우터로 간주된다. 경로와 http method가 지정되어 있다.

app.get('/', (req, res, next) => {
 
});

error handler 는 다른 미들웨어와 라우터 호출을 정의한 후 마지막으로 정의해야 한다.

즉, 순서에 영향을 받는다.

// router
app.get('/', (req, res, next) => {
  next();
});
 
// error handler
app.use((err, req, res, next) => {
 
});
 
app.listen(3000, function() {
 
});

next(err) 를 호출할 경우 중간에 등록된 미들웨어를 건너 뛰고 error handler 부분으로 건너가게 된다.

즉 next 인자로 new Error() 객체를 보내든 string 값을 보내면 에러로 감지하여 error handler로 보낸다.

// A router
app.use((req, res, next) => {
  // next(new Error('error message'))
  next('error message');
});
 
// B router - skip
app.use((req, res, next) => {
  console.log('B router');
});
 
// error handler
app.use((err, req, res, next) => {
  // print: error message
  console.log(err);
});

next() 혹은 next('route') 호출시 다음 미들웨어 혹은 라우터로 흐름을 넘긴다.

아래 예제는 /users/0 로 접근한 경우 next('route')를 통해 다음 라우터로 전달하게 된다.

/users/1 로 접근한 경우 next()를 통해 다음 미들웨어로 흐름을 넘겨 'next() call' 이 출력된다.

아래 예제는 router handler를 통해 하나의 경로에 2개의 route를 설정한 경우이다.

app.get('/users/:id', ( req, res, next) => {
  if(req.params.id == 0) next('route')
  else next()
}, (req, res, next) => {
  res.send('next() call')
});
 
app.get('/users/:id', ( req, res, next) => {
  res.send("next('route') call")
})

정리

error handler 에 대해 알아보았다. 개발을 하다보면 에러 처리를 하는 법이 아니라 어떻게 하는게 좋은지에 대한 best practice에 대해 궁금하게 된다.

블로그를 찾던 중 error handler에 대한 best practice 내용이 담겨 있다. 나중에 한번 보길 바란다.

https://www.joyent.com/node-js/production/design/errors

'Framework > Nodejs' 카테고리의 다른 글

express-session  (0) 2018.01.03
pm2에 대해 알아보자  (0) 2017.10.06
[마이크로서비스] 세네카와 익스프레스 연동  (0) 2017.10.02
[마이크로서비스] Seneca  (0) 2017.10.02
비동기와 CPU Bound  (0) 2017.09.04

ajax 란?

ajax란 Asynchronous Javascript and XML의 약자로 비동기적으로 서버와 브라우저가 데이터를 주고 받는 방식을 의미한다.

ajax 사용하기

먼저 아래의 예제를 보자. 아래는 data에 user에 대한 정보를 담아 유저를 생성하는 API를 호출하는 간단한 예제이다.

let data = {
  userId: userId,
  userPw: userPw
};
data = JSON.stringify(data);
 
let url = "http://localhost:3000/users";
 
let xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-type', "application/json");
xhr.send(data);
 
xhr.addEventListener('load', function() {
  let result = JSON.parse(xhr.responseText);
 
  switch(xhr.status) {
    case 200:
      // 성공 처리
      break;
    case 404:
      // 예외 처리
      break;
  }
 
  // console.log(xhr.responseText);
  // console.log(xhr.status);
  // console.log(result);
});

먼저 XMLHttpRequest 객체를 생성한다.

let xhr = new XMLHttpRequest();

open() 를 통해 POST, GET, PUT, DELETE 인지 정할 수 있다.

세 번째 인자로 true를 주면 async를 의미하고 false를 주면 sync를 의미한다.

xhr.open('POST', url, true);

send()를 통해 data를 전송한다.

xhr.send(data);

load 이벤트를 등록하여 서버로부터 response가 올 때의 행동을 구현할 수 있다.

xhr.status 는 서버로부터 전송받은 status code를 의미한다. 상황에 맞게 처리하면 된다.

xhr.responseText 는 서버로부터 받은 데이터를 의미하는데 String 형태로 받기 때문에 상황에 따라 JSON 형태로 파싱하여 사용해야 한다.

xhr.addEventListener('load', function() {
  let result = JSON.parse(xhr.responseText);
 
  switch(xhr.status) {
    case 200:
      // 성공 처리
      break;
    case 404:
      // 예외 처리
      break;
  }
});

정리

ajax를 통해 동적인 웹 브라우저를 구현할 수 있게 되었다. 하지만 직접 사용하다보니 jquery의 한계점을 조금은(?) 경험하게 되었다.

DOM 이 수정되면 기존의 jQuery는 알지 못하여 이벤트를 매번 관리를 해주어야 한다. ajax 로직이 생각보다 커졌다. 프론트 엔드 프레임워크를 사용하면 이 문제점을 보완할 수 있다고 하니 프레임워크를 공부해봐야 겠다.

'IT > FrontEnd' 카테고리의 다른 글

XMLHttpRequest 간단 소개  (0) 2018.03.23

Session 이란?

서버가 해당 서버에 접근하는 클라이언트를 식별하는 방법 중 하나

서버는 접근하는 클라이언트에게 response-header field인 set-cookie 값으로 클라이언트 식별자인 session-id를 발행한다.

HTTP Session 동작 순서

클라이언트가 서버로 http 요청을 할 때, 서버에서 request-header field인 cookie를 확인해 해당 session-id를 보내왔는지 확인한다.

만약 없다면 session-id를 생성해 클라이언트에게 response-header field에 set-cookie 값으로 session-id를 발행한다.

이 때 발행된 쿠키는 메모리(브라우저)에 저장되며, 세션 종료시 같이 종료된다.

Node js에서 Session 관리

Node.js에서는 express-session이라는 모듈을 사용한다.

npm install --save express-session

우선 간단한 예제를 보자. 아래 예제는 express-session 미들웨어를 추가하는 예제이다.

  • secret : 세션 식별자를 쿠키에 저장하기 전에 암호화하기 위해 필요한 옵션

  • resave : 요청이 왔을 때 세션을 수정하지 않고 세션을 다시 저장소에 저장 여부

  • saveUninitialized : 초기화 되지 않은 세션을 강제로 저장.

const express = require('express');
const app = express();
const session = require('express-session');
 
app.use(session({
    secret: 'mySecret',
    resave: false,
    saveUninitialized: true
    // cookie: {secure: true }
}));

위에서 말했듯이 session을 사용하게 되면 브라우저에 세션에서 할당되는 session-id 값을 지닌 쿠키가 생성된다.



처음에는 저장된 쿠키가 없지만 아래의 코드를 추가시켜 보자. express-session 미들웨어를 추가시켰고 해당 미들웨어에서 세션에 대한 정보를 req.session으로 받는다.

app.get('/login', (req, res) => {
  var session = req.session;
 
  session.userId = 'test';
  session.userPw = '1234';
 
  res.json(req.session);
});

http://localhost:3000/login 으로 접속하여 보자.

쿠키가 생성이 되었음을 확인할 수 있다. connect.sid라는 이름으로 value는 secret 값이 추가되어 암호화된 임의의 문자열로 저장이 되어있을 것이다.


아래 코드를 추가시키고 /home으로 접속하면 저장된 쿠키를 통해 저장된 세션으로부터 userId와 userPw를 가져올 수 있다.

app.get('/home', (req, res) => {
  var session = req.session;
 
  res.json({
    userId: session.userId,
    userPw: session.userPw
  })
});
 

쿠키를 삭제 하고 싶다면 아래의 코드를 참조하자.

app.get('/delete', (req, res) => {
  req.session.destroy(function(err) {
    if(err) {
      res.end(err);
    }
 
    res.json({
      message: "success delete"
    })
  })
});

이외에도 cookie의 maxAge 를 변경할 수도 있다.

app.get('/maxage', (req, res) => {
  var hour = 3600000;
 
  req.session.cookie.expires = new Date(Date.now() + hour);
  req.session.cookie.maxAge = hour;
  req.session.userId = "test3";
 
  res.json(req.session);
});

정리


express-session을 사용하면 session 기반 인증 서버를 구현할 수 있다.

추가적으로 express-session npm site 에 접속하면 각 옵션들에 대한 자세한 정보가 있으니 확인해보면 좋을 것 같다.

곧 포스팅할 passport 역시 session을 기반으로 하기 때문에 session, cookie에 대한 이해를 위해 추가적으로 검색을 해보는 것도 좋을 것 같다.

'Framework > Nodejs' 카테고리의 다른 글

error handler  (0) 2018.01.14
pm2에 대해 알아보자  (0) 2017.10.06
[마이크로서비스] 세네카와 익스프레스 연동  (0) 2017.10.02
[마이크로서비스] Seneca  (0) 2017.10.02
비동기와 CPU Bound  (0) 2017.09.04

MVC 란?

mvc는 model-view-controller의 약자로 소프트웨어 공학에서 어플리케이션을 구성하는 패턴(pattern) 중 하나이다.

eclipse 에서 Spring MVC 기본 예제 작성

먼저 New -> Other -> Spring Legacy Project -> Spring MVC Project 로 프로젝트를 생성한다.


패키지 이름을 설정한다. com.bbo.[]에서 []에 해당하는 부분이 어플리케이션을 구분하는 Context가 된다.

기본 예제를 통해 MVC의 동작 방식을 살펴보도록 하자.


Spring에서 MVC의 동작 방식과 코드 살펴보기

우선 다음의 사진을 보도록 하자.


(1) 우선 유저로부터의 HTTP Request는 DispatcherServlet이 담당한다.

아래 코드는 web.xml에서 DispatcherServlet을 매핑하는 코드이다. 요청된 url로 / 와 매칭이 되면 appServlet 이라는 이름의 DispatcherServlet으로 넘겨준다.

// web.xml
 
<servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

(2) 넘겨받은 DispatcherServlet은 요청된 URL을 HandlerMapping 오브젝트에 넘기고 호출 대상의 Controller를 얻어 URL에 해당하는 메서드를 실행한다.

아래 코드는 HandlerMapping을 동작하게 하는 코드이다.

/WEB-INF/spring/appServlet/servlet-context.xml 을 이용하여 필요한 컨트롤러를 찾는다.

// web.xml
 
<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

com.bbo.myapp 이라는 패키지에서 컨트롤러를 스캔하는 것을 의미한다.

// servlet-context.xml
 
<context:component-scan base-package="com.bbo.myapp" />

(3) Controller 오브젝트는 비즈니스 로직을 처리하고 뷰에 전달할 오브젝트를 Model 오브젝트에 저장한다. 끝으로 Controller 오브젝트는 처리 결과에 맞는 View 이름을 반환한다.

아래 예제는 http://localhost:8181/myapp 이라고 HTTP Request 가 오면 value="/"이므로 아래 로직을 수행하게 된다.(처음에 패키지 이름 설정시 com.bbo.myapp라면 myapp이 어플리케이션을 구분하는 context가 되므로 myapp까지 root url 이 된다.)

model.addAttribute 를 이용하여 serverTime이라는 Key 에 formattedDate Value를 설정하여 Model 오브젝트에 저장한다.

마지막으로 home 이라는 View name 을 return 한다.

// HomeController.java
 
@RequestMapping(value = "/", method = RequestMethod.GET)
    public String home(Locale locale, Model model) {
        logger.info("Welcome home! The client locale is {}.", locale);
 
        Date date = new Date();
        DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
 
        String formattedDate = dateFormat.format(date);
 
        model.addAttribute("serverTime", formattedDate );
 
        return "home";
    }

(4) ViewResolver 로부터 해당 View 오브젝트를 얻는다.

예시를 통해 View의 path는 아래 정의된 prefix + "home" + suffix 가 된다.

// servlet-context.xml
 
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <beans:property name="prefix" value="/WEB-INF/views/" />
    <beans:property name="suffix" value=".jsp" />
</beans:bean>

(5) View 오브젝트에 화면 표시를 Request 한다.

(6) View는 Model 오브젝트로부터 화면 표시에 필요한 오브젝트를 가져와 화면 처리를 실행한다.

정리

eclipse를 통해 spring MVC 프로젝트를 생성하고 기본 예제를 통해 기본적인 동작 방식에 대해 알아보았다.

DispatcherServlet, HandlerMapping, ViewResolver, Model, View 오브젝트는 Spring MVC가 제공해주는 오브젝트이다.

이것저것 구조에 대해서 설명하기 위해 코드를 살펴보았지만 우리는 Controller와 해당 로직, 그리고 뷰를 코딩하면 된다.

'Framework > Spring' 카테고리의 다른 글

Bean의 Scope 지정  (0) 2017.12.28
bean 파일을 이용한 AOP 적용  (0) 2017.12.28
Environment를 이용한 bean 설정  (0) 2017.12.27
Bean 파일을 이용한 DI  (0) 2017.12.27

비트 연산이란?

비트 연산이라 함은 &, |, ~, ^ 을 이용한 연산을 의미한다.

비트 연산은 언제 사용하는가?

우선 비트 연산을 사용하면 bit라는 최소 정보단위로 다른 추상화된 정보들과는 달리 재현의 과정을 거치지 않는다.(그렇기에 속도는 빠르다)

즉, 각 비트들의 0 혹은 1이라는 값을 특정 의미를 가지는 플래그로 활용한다.

비트 연산 예시

우선 아래의 예시를 보자.

const OPTION_A = 0x01;
const OPTION_B = 0x02;
const OPTION_C = 0x04;
const OPTION_D = 0x08;

위의 OPTION A, B, C, D의 값은 16진수로 표현하였다.

즉 16진수는 4비트로 표현이되며 A는 0001(1), B는 0010(2), C는 0100(4), D는 1000(8)을 의미한다.

만약 1바이트(8비트)를 표현하고 싶다면 어떻게 해야 될까?

아래의 예제를 보며 이해를 해보길 바란다.

const OPTION_E = 0x10;

E는 0001 0000을 의미한다. 추가적으로 비트를 늘릴 때는 위의 원리를 이용하면 된다.

해당 비트를 1로 변경할 때는 아래의 예제를 참고하자.

var flag |= OPTION_A

해당 비트가 1인지 확인할 때는 아래의 예제를 참고하자.

var flag &= OPTION_A

해당 비트를 0으로 변경할 때는 아래의 예제를 참고하자.

var flag &= ~(OPTION_A)

정리

비트 연사을 사용하면 하나의 정수로 여러 플래그의 표현이 가능하다.

비트 연산은 알고리즘 문제에 대해서도 많이 사용되므로 정확히 이해하고 넘어가면 좋을 것 같다.

'Language > Javascript' 카테고리의 다른 글

Name Spacing Pattern  (0) 2017.07.28
promise - 2 [Promise 사용 시 주의할 것]  (0) 2017.07.28
promise - 1 [then, catch, resolve, reject]  (0) 2017.07.28
closure  (0) 2017.07.28
scope - 3 [변수 숨기기]  (0) 2017.07.28

도큐먼트들로부터 특정 필드 삭제하기

만약 user이라는 모델에 특정 필드 A를 삭제하고 싶다면 mongodb 에 다음과 같은 명령어를 사용할 수 있다.

db.users.update({}, {$unset: {A:1}})

위의 명령어가 의미하는 것은 그냥 아무거나 하나의 도큐먼트의 A 필드를 삭제하겠다는 것을 의미한다.

만약 다수의 도큐먼트를 처리하기 위해서는 다음과 같이 옵션을 추가하면 된다.

db.users.update({}, {$unset: {A:1}}, {multi: true})

정리

$unset 옵션은 특정 필드의 삭제를 의미한다.

반대로 $set은 특정 필드의 값을 정한다.

위의 원리를 이용하면 특정 필드의 값도 한꺼번에 set할 수 있다.

아래 예제는 모든 도큐먼트에 A 필드에 3이라는 값을 set 하는 예제이다.

db.users.update({}, {$set: {A:3}}, {multi: true})


'IT > Mongodb' 카테고리의 다른 글

서브 도큐먼트 검색 후 값 변경  (0) 2018.02.07
$elemMatch vs $in  (0) 2017.10.31
ISO Date 값이 한국 시간과 다를때  (1) 2017.10.31
스키마 모델링 어떻게 해야 하나  (0) 2017.10.21

+ Recent posts