TDD(Test Driven Development)는 QA를 떠나 서비스를 개발할 때 필수적으로 거쳐야 한다.

NodeJS에서 TDD를 수행하기 위해 mochachai를 이용할 수 있다. 이 외에도 좋은 라이브러리가 많으니, 각자 취향에 맞는 라이브러리를 사용하면 된다.

TDD를 공부하기 위해 간단한 To Do List를 함께 제작할 것이다.


Install Dependencies

npm install mocha chai chai-as-promise **--save-dev**|--global

Mocha

Mocha는 Javascript를 Test할 수 있는 하나의 프레임워크다. Mocha를 사용하여 우리가 수행할 단위 테스트 환경을 쉽고 간결하게 작성할 수 있으며 작성한 함수 등에 대하여 스펙에 맞게 테스트를 수행할 수 있다.

또한 Mocha가 제공하는 describeit을 이용하여 테스트에 대한 설명을 작성하고 구분지음으로써 어떤것에 대한 내용인지 확인할 수 있다.

Chai

Chai는 assertion 라이브러리로 Mocha와 함께 쓰이는 라이브러리다. Chai는 Mocha를 통해 수행한 테스트의 결과가 내가 기대한 값인지 테스트할 수 있도록 assertion을 제공한다.

특히 Chai는 사람이 이해할 수 있는 구조로 syntax가 작성되어 있기 때문에 사용하기 편하다.

우리는 chai와 chai-as-promise를 함께 사용하여 NodeJS에서 발생하는 Asynchronous Function에 대한 테스트도 수행할 것이다.

Setting Environment

이제 Mocha와 Chai를 이용한 테스트 환경을 구축하려 한다.

NodeJS상에서 ES6를 이용하여 프로젝트를 만들것이다.

NodeJS: 16.16.0
ECMAScript6
Language: Javascript ( not typescript )
mkdir todolist
cd todolist
npm install mocha chai chai-as-promised --save-dev
npm install express http fs
mkdir test route public middleware model

Mocha Chai 설치가 끝났다면, package.json 파일에 다음과 같이 굵은 내용을 추가해 준다.

{
  "name": "tdd-todolist",
  "version": "0.0.1",
  "description": "Study Test Driven Develop in NodeJS - To Do List",
  "main": "starter.js",
  **"scripts": {
    "test": "mocha test/**/**.spec.js",
        "start": "starter.js"
  },**
  "author": "eugene",
  "devDependencies": {
    "chai": "^4.3.7",
        "chai-as-promised": "^7.1.1",
    "mocha": "^10.2.0"
  },
  "dependencies": {
    "express": "^4.18.2",
    "fs": "^0.0.1-security",
    "http": "^0.0.1-security"
  },
  **"type": "module"**
}

이 때, “scripts”아래의 “test”는 test 디렉토리 아래의 모든 *.spec.js를 mocha를 통해 테스트한다. 는 뜻이다.

이를 통해 생성된 프로젝트 구조는 다음과 같다.

Simple Test

이제 간단한 테스트를 통해 환경이 잘 구축되었는지 보려한다. model/user.js, test/user.spec.js를 통해 user가 잘 동작하는지 확인해 볼 것이다.

model/user.js

user.js는 간단하게 전달받은 사용자의 이름과 비밀번호를 바탕으로 객체를 생성하고 다음과 같은 함수를 가진다.

  • toObject(): User 정보를 Object 형태로 출력한다.

  • promise(shouldResolve, shouldError): promise 함수를 테스트한다.

    • shouldResolve: Boolean
      • caller에게 resolve(true|false)를 반환할 것인지 설정한다.
    • shouldError: Boolean
      • caller에게 error를 반환할 것인지 설정한다.

    toObject() Method를 통해 user의 정보를 출력하는 클래스다. 여기에 추가로, Javascript의 특징인 Asynchronous Function의 테스트를 수행하는 promise(resolve, shouldError)함수가 존재한다.

export default class User {
    constructor(name, password){
        this.name = name;
        this.password = password;
    }

    toObject(){
        return {
            name: this.name,
            password: this.password
        }
    }

        promise(shouldResolve, shouldError){
        return new Promise((resolve, reject) => {
            if(shouldError){
                return reject();
            }

            if(shouldResolve){
                return resolve(true);
            }
            return resolve(false);
        })
    }
}

test/user.spec.js

user.spec.js는 위에서 생성한 model/user.js가 정상적으로 동작하는지 확인하는 Unit Test를 제공한다.

따라서 우리는 User클래스에 존재하는 toObject()함수와 promise(shouldResolve, shouldError)함수를 모두 테스트 할 것이다.

chai와 chaiAsPromised를 사용할 것이기 때문에, 다음과 같은 구문을 최상단에 작성하고, 우리가 테스트할 클래스를 가져온다.

import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
chai.use(chaiAsPromised);

const expect = chai.expect;
const assert = chai.assert;

import User from '../model/user.js';

그 다음 줄 부터 test를 수행할 내용을 작성하면 되는데, 그 모양은 다음과 같다.

  • describe를 통한 Unit Test 단위 정의 예시

      describe('테스트를 수행에 대한 최상위 이름', () => {
          describe('그 다음 이름', () => {
              describe...
          });
      });
  • it을 통한 테스트 수행 예시

      describe('최상위 이름', () => {
          it('함수 확인', () => {
              expect(확인할 대상).to.be.a('function')
          });
      });

이를 바탕으로 우리가 수행할 테스트 내용을 작성하면 다음과 같다.

먼저 User class를 정상적으로 사용할 수 있는지 expect().to.be.a('function')expect().to.be.a.instanceOf(Parent)를 통해 확인한다.

describe('"Up"', () => {
    it('should be exist', () => {
        expect(User).to.be.a('function');
    });

    it('should be a class', () => {
        const user = new User();
        expect(user).to.be.a.instanceOf(User);
    });
});

다음으로, User class 내에 모든 Method가 정상적으로 동작하는지 확인한다. 이 때, 우리는 async 함수를 따로 갖고 있으므로 synchronous와 asynchronous를 구분해서 수행하겠다.

const user = new User('eugene', 'password');
/* Synchronous 함수 */
describe('"Synchronous"', () => {
/* toObject() 함수를 통해 user의 이름과 비밀번호가 잘 설정되는지 확인한다. */
    it('toObject()', () => {
        const obj = user.toObject();
        expect(obj.name).to.be.equal('eugene');
        expect(obj.password).to.be.equal('password');
    })
});

/* Asynchronous 함수 */
describe('"Asynchronous"', () => {
    const promise = user.promise;
/* promise함수가 정말 promise 함수인가? */
    it('"promise" is promise function', () => {
        const _promise = promise();
        expect(_promise.then).to.be.a('Function');
        expect(_promise.catch).to.be.a('Function');
    })
/* promise함수에서 내가 설정한 인자를 전달하면, 그 결과가 예상대로 반환되는가 */
    it('"promise()" should be resolved', async () => {
        promise( true ).then(
            (data) => expect(data).to.be.a.true,
            (error) => expect(error).to.be.a.false
        );
    })
/* promise함수에서 내가 설정한 인자를 전달하면, 그 결과가 예상대로 반환되는가 */
    it('"promise()" should be a false', async () => {
        promise( false ).then(
            (data) => expect(data).to.be.a.true,
            (error) => expect(error).to.be.a.false
        );
    })
/* promise함수에서 내가 의도한 에러가 잘 발생하는가 */
    it('"promise()" should be a error', () => {
        expect(promise( false, true )).to.be.rejectedWith(Error);
    })
});

이 내용들을 모두 포함하면, test/user.spec.js가 완성된다.

import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
chai.use(chaiAsPromised);

import User from '../model/user.js';

const expect = chai.expect;
const assert = chai.assert;

describe('User module', () => {
    describe('"Up"', () => {
        it('should be exist', () => {
            expect(User).to.be.a('function');
        });

        it('should be a class', () => {
            const user = new User();
            expect(user).to.be.a.instanceOf(User);
        });
    });

    describe('"Method Check"', () => {
        const user = new User('eugene', 'password');
        describe('"Synchronous"', () => {
            it('toObject()', () => {
                const obj = user.toObject();
                expect(obj.name).to.be.equal('eugene');
                expect(obj.password).to.be.equal('password');
            })
        });

        describe('"Asynchronous"', () => {
            const promise = user.promise;
            it('"promise" is promise function', () => {
                const _promise = promise();
                expect(_promise.then).to.be.a('Function');
                expect(_promise.catch).to.be.a('Function');
            })

            it('"promise()" should be resolved', async () => {
                promise( true ).then(
                    (data) => expect(data).to.be.a.true,
                    (error) => expect(error).to.be.a.false
                );
            })

            it('"promise()" should be a false', async () => {
                promise( false ).then(
                    (data) => expect(data).to.be.a.true,
                    (error) => expect(error).to.be.a.false
                );
            })

            it('"promise()" should be a error', () => {
                expect(promise( false, true )).to.be.rejectedWith(Error);
            })
        });
    });
});

Mocha를 통한 test 수행

이제 작성한 test/user.spec.js가 잘 되는지 확인하면 된다. 명령어는 우리가 package-.json에 작성해 놓은대로, npm test명령어를 통해 수행할 수 있다.

npm test

만약 결과가 내 예상(user.spec.js에 기술한 것)과 다르다면, 다음과 같이 테스트에 실패한 부분에서 에러가 발생한다.

이렇게 오늘 TDD를 위한 기초에 대해 공부해봤다. 다음 장 에서는 todolist 개발을 함께하면서 어떻게 TDD를 해야하는지 배워보겠다.

오늘은 오주주의 맥세이프 거치대를 리뷰하려 한다.

아이폰 13프로를 쓰면서, 가장 편하다고 생각한 기능 중 하나가 맥세이프인데, 오주주에서 이를 활용한 거치대 체험단을 모집한다 하여 신청하였고, 운이 좋아 체험단에 선정되어 해당 제품을 사용해 볼 수 있게 되었다.


핸드폰 거치대를 사용해 본 많은 분들은 느끼겠지만, 핸드폰을 거치대에 고정 시킬때 꽤나 힘들다. 단순히 휴대폰을 고정하기 위한 고정 작업의 귀찮음 뿐만 아니라, 휴대폰을 거치하거나 탈착할 때 혹시 기스가 나진 않을까하는 조마조마함을 느껴봤을 거라 생각한다.

나 또한 예전에 거치대를 사용했지만, 위와 같은 이유로 더이상 사용하지 않고 있었다.

출처: 오주주 홈페이지

특히, 휴대폰을 사용하기 위해 거치대에서 핸드폰을 분리해야 하는 그 귀찮음이란!!

출처: 오주주

그런데, 오주주에서 이를 해결하는, 사용자의 요구사항을 잘 분석한 맥세이프 거치대를 내놓은 것이다!!!


체험단 신청 후, 바빠서 결과를 확인하지 못하고 있었는데, 나에게 이런 행운의 메시지가 와서, 당첨된 사실을 알게 되었다.

 

감사합니다 담당자님..

제품 패키지 및 구성품

제품 구성품은 정말 심플하다. 오주주도 지구 보호에 앞장 서는지, 박스 패키지부터 친환경(재활용)소재를 사용한 것이 눈에 띄었다.

이게, 사진으로 봐서는 이게 무슨 친환경이야? 할 수 있지만, 친환경 패키지를 많이 써 본 사람들은 해당 박스를 딱 받았을 때, 어!! 하는 느낌을.. 받을수 있다.

조립 방법

조립 방법이 끼우고 끼우면 끝! 이라고 나와있는데, 끼우고! 까지는 인정하지만, 그 뒤의 끼우면!은 살짝, 팁이 필요하다.

맥세이프의 머리에 보면, 조을 수 있게 되어있는데 해당 부품을 풀고, 거치대에 끼운 뒤, 잠궈줘야 한다. 바로 끼우면 부서질 수 있으니 주의해야 한다!!


설치 완료 모습

생각보다 거치대의 클램프 고정 나사(?)가 길어서, 당황하긴 했지만,,, 결국 옷장에 고정시켜 나도 누워서 휴대폰을 할 수 있게 됐다!!

이제 나는 밤을 잃었..

거치대가 상하 좌우, 심지어 핸드폰이 걸려있는 헤드도 360도 회전이 되어서.. 정말 어느 자세로든 휴대폰을 즐길 수 있게 되었다.

 


설치가 쉽고, 강력하게 고정되며, 편리하게 사용할 수 있는 거치대.
이 제품과 함께라면, 여러분의 밤은 이미 빼앗긴 상태

총점: ★★★★★ 별이 다섯 개~

장점

1. 누워서 휴대폰 할 때 전혀 불편하지 않다. (거치대의 포지션을 마음껏 수정할 수 있음)

2. 설치가 진~~짜 쉽고, 강력하게 고정된다.

3. 더이상 휴대폰을 머리위에 떨어트릴 걱정하지 않아도 된다.

단점: 없음!!

 

오주주 구매 링크

 

오주주 맥세이프 자바라 거치대 : 오주주

자바라 거치대, 침대 핸드폰 거치대, 누워서 핸드폰 거치대, 아이폰 거치대, 탁상용 거치대, 집게형 거치대

smartstore.naver.com

본 포스팅은 맥쓰사에서 체험단으로 선정되어 오주주로부터 제품을 지원받아 작성한 글 입니다.

 <VirtualHost *:80>
         ServerName [hostname] # www.myhost.com 등
         ErrorLog ${APACHE_LOG_DIR}/error-comnet.log
         CustomLog ${APACHE_LOG_DIR}/access.log combined
         ProxyRequests Off
         ProxyPreserveHost On
         ProxyPass [uri] [nodejs 주소]
         ProxyPassReverse [uri] [nodejs 주소]
 </VirtualHost>

 

1. Xcode 설치

2. $ open /Applications/Xcode.app/Contents/Developer/Applications/Simulator.app

여자친구가 버즈2를 사용중인데, 기본으로 제공되는 이어팁이 귀에서 너무 잘 빠져 이어팁을 알아보던 도중, 삼공카에서 디오핏 메모리폼 이어팁 체험단을 모집하고 있길래 신청했는데, 운이 좋아 당첨됐다!
그래서 오늘은 해당 제품을 리뷰하려 한다.


제품 박스

일단 제품 박스는 진짜 작다. 불필요한 크기를 줄였고, 그래서 깔끔하게 좋은것 같다.

깔끔한 구성품

디오핏 이어팁의 특징 중 하나가 사이즈마다 내부 색깔이 다르게 되어있어서 한눈에 보기 쉽다는 장점이 있다.

왼쪽부터 L, M, S
버즈2 블랙 장착 사진

버즈2 블랙 색상을 사용중인데 메모리팁 자체가 검은색이라 일체감이 진짜 좋은것 같다.

좌우 착용 사진

장점
1. 메모리 팁의 특성상, 내 귀에 맞게 안착되어 안빠진다.
진짜 귀에서 빠지는 문제때문에 여자친구가 불편함을 감수하고 사용중이었는데, 디오핏 메모리 팁을 사용하면서 이제 안빠진다고 한다!
2. 차음이 잘되어 음질이 더 좋아졌다.
아무래도 귀 구조에 맞게 팁이 착- 되다보니, 주변 소리를 더 잘 차폐하여, 듣고자 하는 미디어에 더 잘 집중할 수 있게 됐다고 한다.
단점
1. 내가 한번 껴 봤는데, 귀지가 많이 붙더라 ㅋㅋㅋ
본 포스팅은 삼성 스마트폰 카페 체험단에 선정되어 제품을 지급받아 작성된 글입니다.
아래 링크를 통해 네이버 스마트 스토어 혹은 쿠팡에서 구매할 수 있다.

갤럭시 버즈2 이어팁 버즈플러스 버즈+ 메모리 폼팁 실리콘 디오핏 Galaxy Buds2 Buds Plus Memory Foamtip S

[오디오 디오핏] One and Only, 당신에게 딱 맞춘 최상의 품질. 디오핏에 오신 걸 환영합니다.

smartstore.naver.com

갤럭시 버즈2 버즈플러스 버즈+ 메모리 폼팁 실리콘 이어팁 디오핏 Galaxy Buds2 Buds Plus Memory Foamtip S

COUPANG

www.coupang.com

위 링크를 통해 쿠팡에서 구매할 경우 "이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."

오늘은~! 아이폰 13 프로 강화유리를 리뷰하려 한다.

아이폰 강화유리로 ESR, 돔글라스 EZ에 이어 세번째로 사용해보는 제품인데, 만족도가 가장 높은 것 같다.


구성품 사진

일단 강화유리가 두매 들어있어서 놀랐다. 돔글라스 EZ의 경우 강화유리가 한 장 들어있어서 비싼감이 있었는데, 오하이는 그에 비해 두장 들어있어서 좋았다.

무엇보다 ESR이나 돔글라스와 비교했을 때 부착틀이 너무 잘 되어 있다.

부착틀에 강화유리를 끼우고, 부착 틀을 핸드폰에 올리면 된다. 벗어날 일이 없다.

타 제품, ESR, 돔글라스의 경우에는 부착 틀이 있어도 내가 직접 강화유리의 위치를 맞춰야 하기 때문에 어긋나는 문제가 있었지만, 오하이의 경우에는 부착틀에 강화유리를 아예 끼우고, 핸드폰에 틀을 맞추는 방식이기 때문에 전혀 어긋날 문제가 없다.

1번 가이드 필름을 때고 핸드폰에 부착 틀을 올리면 위 사진처럼 쫘아아악 붙는다!

부착완료 사진

시인성도 좋다. 사실 강화유리를 쓰면서 시인성이 문제된 경험은 없었고, 오하이도 마찬가지로 본래의 디스플레이를 잘 표현한다.


장점

1. 부착이 정말 쉽다. 정말 너무너무너무 쉽다. 오하이 제품만 살듯하다.

2. 테두리가 검은색으로 마감되어 있는데, 일체감이 진짜 너무 좋다. ESR의 테두리가 흰 것은 강화유리를 붙였다는 느낌이 있고, 돔글라스는 마찬가지로 검은색이지만 가격이 비싸서..

단점

1. 내 제품이 코팅 불량인지는 모르겠는데, 아래 첨부한 사진의 부분에 계속 일정한 얼룩이 남는다. 이것 말고는 없다.

본 포스팅은 아사모로부터 체험단으로 선정되어 제품을 지원받아 작성한 글 입니다.

 

Shop Ohi : 네이버쇼핑 스마트스토어

편리한 IT라이프에 건내는 인사, 오하이

smartstore.naver.com

 

 

[RESTful API 설계하기] 1. RESTful과 API

[RESTful API 설계하기] 2. REST 특징 [RESTful API] 1. RESTful과 API 어떤 서비스를 개발할 때 본래 필수적인 기능은 아니었지만, 이제는 필수적인 기능이 되어버린 API와 관련하여 글을 작성하려 한다. 이 중

dev-whoan.xyz

 

[RESTful API 설계하기] 2. REST 특징

[RESTful API] 1. RESTful과 API 어떤 서비스를 개발할 때 본래 필수적인 기능은 아니었지만, 이제는 필수적인 기능이 되어버린 API와 관련하여 글을 작성하려 한다. 이 중 굉장히 많이 쓰이고, 한번도 안

dev-whoan.xyz

 

[RESTful API 설계하기] 3. RESTful API 데이터 조회 GET Method

[RESTful API 설계하기] 1. RESTful과 API 어떤 서비스를 개발할 때 본래 필수적인 기능은 아니었지만, 이제는 필수적인 기능이 되어버린 API와 관련하여 글을 작성하려 한다. 이 중 굉장히 많이 쓰이고,

dev-whoan.xyz

오늘은 POST Method의 기능에 대해 알아보자.

POST Method 데이터의 생성부분을 담당한다. 다시 말하면, 데이터 조회의 조건이 필요없다는 말이다.

생성 대상이 되는 Data Set에 대하여 새로운 데이터의 내용을 채워주면 된다.

POST /dataset --data '새로운 데이터의 내용'

즉 사용자에 대한 dataset이 user이고, 채워야 하는 내용이 id, pw, name이 전부라면 다음과 같은 생김새를 갖게 된다.

POST /user --data '{"id":"gildong", "pw":"password", "name":"홍길동"}'
Data Set은 URI,
데이터 내용은 Body Parameter

간혹가다 사설 RESTful API의 경우 Body Parameter를 Query String으로 보내는 경우가 있는데, 어.. 매우 좋지 않다. Query String으로 보낼 경우, 외부에 노출되기 때문에 보안적 측면에서 매우매우 아주아주 좋지 않은 구현이다.

사용자 로그인 기능을 구현할 때, 사용자 정보를 Body Parameter에 담아서 보내는 것을 생각하면, 그 이유를 떠올리기 쉬울 것이다.

Body Parameter는 JSON Type으로!

Body Parameter는 어느 플랫폼에서 보내느냐에 따라 획득할 수 있는 방법이 다르다. Nodejs 즉 React에서 전송된 데이터와 JSP를 통해 전송된 데이터를 구하기 위한 방식에 약간의 차이가 있기 때문에, (적어도 Java의 Dynamic Web Application은 그러했다.) 될 수 있으면 데이터 생성 요청에 대한 컨텐츠 타입을 미리 서버에서 JSON만 허용하도록 설계해 놓는게 편하다.

따라서 우리가 편하게 데이터를 조작할 수 있는 JSON Type으로 요청을 받도록 하는게 제일 좋다.


글이 짧아서 실망했을 수 있는데, 데이터 조회를 제외하고는 우리가 흔히 했던 일반적인 API를 설계하는 것과 비슷하다. 다만 RESTful API라는 객체의 입장에서 다른 부가기능적인 요소를 생각해야 하고, 그것은 POST Method를 설계하기 위해 필수조건이 아니기 때문에 오늘 POST Method의 설계는 여기서 마치겠다.

그렇다고 해서 다른 Methods에서 다 되는데 POST만 안되게 하라는건 아니다. 다만 POST의 의무가 아닌 RESTful API 객체의 의무라는 뜻이다.

 

 

 

[RESTful API 설계하기] 1. RESTful과 API

어떤 서비스를 개발할 때 본래 필수적인 기능은 아니었지만, 이제는 필수적인 기능이 되어버린 API와 관련하여 글을 작성하려 한다. 이 중 굉장히 많이 쓰이고, 한번도 안 써본 사람은 있어도 한

dev-whoan.xyz

 

[RESTful API 설계하기] 2. REST 특징

[RESTful API] 1. RESTful과 API 어떤 서비스를 개발할 때 본래 필수적인 기능은 아니었지만, 이제는 필수적인 기능이 되어버린 API와 관련하여 글을 작성하려 한다. 이 중 굉장히 많이 쓰이고, 한번도 안

dev-whoan.xyz

 

[RESTful API 설계하기] 4. RESTful API 데이터 생성 POST Method

[RESTful API 설계하기] 1. RESTful과 API [RESTful API 설계하기] 2. REST 특징 [RESTful API] 1. RESTful과 API 어떤 서비스를 개발할 때 본래 필수적인 기능은 아니었지만, 이제는 필수적인 기능이 되어버린 AP..

dev-whoan.xyz

RESTful API중 데이터 조회를 위한 방법은 GETHEAD 두 가지가 있다. 

Method가 조회를 담당하는데, 이중 HEAD는 실제 response body가 없는 녀석이다.

GET
{
  "took":"12",
  "code":"200",
  "response":"HTTP 1.1 200 OK",
  "Content-Type":"application/json;charset=UTF-8",
  "Content-Length":"79",
  "users" : [ {
    "address" : "dev.whoan@gmail.com",
    "name" : "dev.whoan",
    "user_SEQ" : "1"
  } ]
}

HEAD

{
  "took":"12",
  "code":"200",
  "response":"HTTP 1.1 200 OK",
  "Content-Type":"application/json;charset=UTF-8",
  "Content-Length":"79"
}

많은 사람들이 API를 생성할 때 다음과 같은 형식을 가지게 설계한다.

1. QueryString

GET /getUsers?name={name}&user_SEQ={seq}&type={type}&print={print}

2. URI Segment

GET /users/name/{name}/user_SEQ/{seq}?type={type}&print={print}
사실 이 방식은 MkWeb을 설계하면서 고안한 방법이고, 앞 글을 보면 내가 준 팁에 그대로 나와있다.

앞서 말한것 처럼 RESTful API는 URI Segment를 지향하고, 주소에 데이터 관련 조작 방식을 나타내지 않는게 좋기 때문에 2. URI Segment방식을 활용하여 설계하는 것이 좋다.

하지만 바로 전 글에서 말한것 처럼 꼭 이를 지킬 필요는 없다. ElasticSearch의 RESTful API같은 경우에는 QueryString중 q 파라미터를 통해 데이터를 조회하는것 처럼, 내가 잘 설계하고, 사람들에게 잘 지침을 주면 된다.

MkWeb의 경우에는 다음과 같이 설계 했다.

 /DATASET/조건1/{조건1값}/조건2/{조건2값}/...?OPTIONS
먼저 데이터 셋을 정의해라

어떤 RESTful API든 간에 우리가 조회하고자 하는 대상이 있다. 해당 대상에 대한 데이터 셋을 먼저 정의하고, URI의 최상단에 선언하자.

그렇게 되면, 우리가 조회하고자 하는 데이터가 무엇인지 한눈에 보기 쉽기 때문이다.

데이터 셋을 정의할 때는, 여러개의 데이터를 하나로* 묶을 수 있고, 한 API당 하나의 데이터만**을 조회하게 할 수 있다.

MkWeb에서는 한 API당 하나의 데이터만을 조회하게 했는데, 이미 MkWeb에서 지원하는 다른 SQL Method에서 여러 데이터를 하나로 묶은 결과를 받을 수 있기 때문이었고, 큰 이유는 API는 데이터를 조작하기 위함인데, 여러 데이터를 통합했을 때 발생하는 데이터는 이러한 목적, 즉 이미 조작된 데이터이기 때문에 약간 다르다고 생각했기 때문이다.

*여러개의 데이터를 하나로: 유저정보 + 직장정보

**한 API당 하나의 데이터: 유저정보, 직장정보 각각 따로

데이터 조건들은 모두 Key-Value 쌍을 이루게 하자

RESTful API를 처음 설계할 때, 다음과 같은 설계를 허용할지 엄청 고민했다.

GET /users/name
MkWeb 자체가 범용적인 사용이 가능하기를 바랬고, 그렇기 때문에 발생한 고민이었는데 결국 의미 없는 고민이었다.

해당 요청의 목적은 요청에 대한 응답이 이름만 반환하기를 바랬던 것인데, 이는 사용자가 요청부분에서 담당할게 아니라, 응답하기 위한 데이터 셋에 name만 반환하도록 설계하면 되는 것 이었다.

딴 말이 길었는데, 조건들이 Key-Value 쌍을 이룸으로써 어떤 데이터 셋에 대한. 조회를 할 때, 정확한 데이터만 집을 수 있도록 함이다. 물론 정확한 데이터, Unique하지 않은 필드의 조회에 대해서는 그 결과에서도 겹치는 부분이 많을 것이다. (동명이인과 같이)

그렇지만, 적어도 쌍을 이루게 함으로써 조회의 정확도를 높이고, RESTful API의 설계 난이도를 낮출 수 있다.

API 조건은 모두 Query String으로

URI Segment에 조회하고자 하는 데이터 셋을 특정짓고, 검색 옵션을 모두 Query String에 넣음으로써 우리는 무엇이 데이터와 관련된 조건이고, 응답에 대한 옵션인지를 한눈에 파악할 수 있다.

API 조건은 예를 들어, 응답을 특정 방식으로 정렬하기를 바라거나, 데이터 조회시 20개만 가져오길 바라는 등, 검색하고자 하는 데이터를 특정하지는 않지만, 데이터 조회에 영향을 미치는 부가 기능을 의미한다.

1.Query String처럼 한곳에 데이터 검색을 위한 조건과 옵션을 모두 때려 넣으면, 어느 것이 데이터 조건이고, 부가기능인지 헷갈리기 때문이다.


그래서 설계를 어떻게 해야 하는가?

사실 많은 사람들이 설계를 어떻게하는지 보기 위해서 이 페이지에 들어왔을거라 생각한다. 나도 처음 RESTful API를 접했을 때 많은 고민을 했고, 특히 MkWeb의 특징을 살리기 위해 가능한 모든 방안을 수용 가능하도록 만들려고 했다. 하지만 RESTful API인 것 처럼, 그리고 이미 RESTful API의 설계에 대해 고민을 해봤다면, 의미 없는 질문인 것을 알 수 있다.

결국, RESTful API를 설계하기 위한 방법론, 즉 설계도면을 구하고자 하는 것이지, 이미 설계도면을 구한 상태에서 설계를 어떻게 해야하는가 하는 질문은, 건축하기 위한 설계도를 구했으나, 내부 자재는 어떤걸 쓸지, 드릴질은 몇번 할지를 묻는것과 같기 때문이다.

이미 프로그래밍을 할 수 있고, 웹 통신을 할 수 있다면, 위 방법들을 통해 적어도, 낮은 수준의 코딩에서라도 GET Method를 구현할 수 있다고 생각한다.


+ Recent posts