3-4. 로컬인증을 위한 API 준비하기

API 컨트롤러 및 라우트 초기설정

회원 인증 api 의 라우트는 /api/auth 로 사용하도록 하겠습니다. 이에 맞춰 디렉토리에 auth 디렉토리를 만들고, 라우트에서 필요한 파일들을 생성하세요.

src/api/auth/auth.controller.js

const Joi = require('joi');
const Account = require('models/Account');

// 로컬 회원가입
exports.localRegister = async (ctx) => {
    ctx.body = 'register';
};

// 로컬 로그인
exports.localLogin = async (ctx) => {
    ctx.body = 'login';
};

// 이메일 / 아이디 존재유무 확인
exports.exists = async (ctx) => {
    ctx.body = 'exists';
};

// 로그아웃
exports.logout = async (ctx) => {
    ctx.body = 'logout';
};

먼저, 추후 구현할 컨트롤러 함수들을 미리 준비하였습니다. 잠시 후 API 테스팅을 해봐야 하므로, 임시적으로 문자열을 보여주도록 설정을 했습니다. 그리고, 추후 필요해질 라이브러리 joi 와, Account 모델도 미리 불러왔습니다.

src/api/auth/index.js

const Router = require('koa-router');
const auth = new Router();
const authCtrl = require('./auth.controller');

auth.post('/register/local', authCtrl.localRegister);
auth.post('/login/local', authCtrl.localLogin);
auth.get('/exists/:key(email|username)/:value', authCtrl.exists);
auth.post('/logout', authCtrl.logout);

module.exports = auth;

3번째 API exists 에서는 :key(email|username) 이 사용되었는데, 이 의미는 key 라는 파라미터를 설정하는데, 이 값들이 email 이나 username 일때만 허용한다는 것 입니다.

src/api/index.js

const Router = require('koa-router');

const api = new Router();
const auth = require('./auth');

api.use('/auth', auth.routes());

module.exports = api;

이렇게 파일들의 초기설정을 하고 난 다음엔 아래 주소들을 테스트해보세요.

이메일 회원가입 구현하기

이메일 회원가입에선, 아이디 존재유무, 이메일 존재유무를 판단해야합니다. 하지만, 일단 계정정보들이 존재해야 이런 작업들도 테스팅을 할 수 있기 때문에 먼저 데이터를 생성하는 부분만 구현을 해보도록 하겠습니다.

이 API 의 주소는 /api/auth/register/local 이며 POST 요청정보는 다음 값들을 받게 됩니다:

{
    username,
    email,
    password
}

먼저 이 요청정보를 검증하는 코드를 작성할것인데요, 아이디는 4~15자의 영소문자이며, 비밀번호는 최소 6자, 이메일은, 당연하게도, 이메일 형식이어야 합니다.

Joi 를 사용하여 검증하는 코드를 작성해보겠습니다.

src/api/auth/auth.controller.js - localRegister

exports.localRegister = async (ctx) => {
    // 데이터 검증
    const schema = Joi.object().keys({
        username: Joi.string().alphanum().min(4).max(15).required(),
        email: Joi.string().email().required(),
        password: Joi.string().required().min(6)
    });

    const result = Joi.validate(ctx.request.body, schema);

    // 스키마 검증 실패
    if(result.error) {
        ctx.status = 400;
        return;
    }
};

그 다음 작업은 이메일 / 유저네임 중복확인 작업이지만 이는 주석처리를 하고 데이터를 한번 생성해본다음에 구현하도록 하겠습니다.
그럼, 곧 바로 데이터를 생성하겠습니다. 우리가 아까 만들었던, Account 모델의 localRegister 함수의 파라미터로, 객체가 들어가도록 설정했었지요? 이 객체의 스키마가 POST 요청정보와 동일합니다.

따라서, 다음과 같이 바로 함수에 전달을 해주면 되겠습니다.

src/api/auth/auth.controller.js - localRegister

// 로컬 회원가입
exports.localRegister = async (ctx) => {
    // 데이터 검증
    const schema = Joi.object().keys({
        username: Joi.string().alphanum().min(4).max(15).required(),
        email: Joi.string().email().required(),
        password: Joi.string().required().min(6)
    });

    const result = Joi.validate(ctx.request.body, schema);

    if(result.error) {
        ctx.status = 400;
        return;
    }

    /* TODO: 아이디 / 이메일 중복처리 구현 */

    // 계정 생성
    let account = null;
    try {
        account = await Account.localRegister(ctx.request.body);
    } catch (e) {
        ctx.throw(500, e);
    }

    ctx.body = account.profile; // 프로필 정보로 응답합니다.
};

코드를 다 작성하셨다면, 다음 요청을 테스트해보세요.

요청

POST http://localhost:4000/api/auth/register/local
{
    "username": "velopert",
    "email": "public.velopert@gmail.com",
    "password": "123123"
}

응답

{
    "thumbnail": "/static/images/default_thumbnail.png",
    "username": "velopert"
}

이렇게 되었다면, 계정생성이 성공한것입니다.

이메일 회원가입 - 중복 아이디/이메일 확인하기

이제 중복되는 아이디/이메일이 있는지 검증을 해보겠습니다. 이때 사용되는 함수는 Account.findByEmailOrUsername 입니다. 데이터 찾기를 시도하고, 만약에 존재하는 데이터가 있으면 409 에러코드로 응답하고, 어떤 키가 중복하는지 알려주도록 설정하겠습니다.

src/api/auth/auth.controller.js - localRegister

// 로컬 회원가입
exports.localRegister = async (ctx) => {
    // 데이터 검증
    const schema = Joi.object().keys({
        username: Joi.string().alphanum().min(4).max(15).required(),
        email: Joi.string().email().required(),
        password: Joi.string().required().min(6)
    });

    const result = Joi.validate(ctx.request.body, schema);

    if(result.error) {
        ctx.status = 400; // Bad request
        return;
    }

    // 아이디 / 이메일 중복 체크
    let existing = null;
    try {
        existing = await Account.findByEmailOrUsername(ctx.request.body);
    } catch (e) {
        ctx.throw(500, e);
    }

    if(existing) {
    // 중복되는 아이디/이메일이 있을 경우
        ctx.status = 409; // Conflict
        // 어떤 값이 중복되었는지 알려줍니다
        ctx.body = {
            key: existing.email === ctx.request.body.email ? 'email' : 'username'
        };
        return;
    }

    // 계정 생성
    let account = null;
    try {
        account = await Account.localRegister(ctx.request.body);
    } catch (e) {
        ctx.throw(500, e);
    }

    ctx.body = account.profile; // 프로필 정보로 응답합니다.
};

이제 아까와 똑같은 정보로 요청을 넣어보세요. 에러가 뜨나요? 그렇다면 username 을 조금 바꾸고 요청을 해보고, 에러가 뜬다면 성공입니다! 그 다음엔 또 다시 이메일을 조금 바꾸고 요청을 다시 해보세요.

이메일 로그인 구현하기

이메일 로그인은 회원가입보단 조금 더 쉽습니다.

먼저, POST 요청 정보부터 검증을 해보도록 하겠습니다.

src/api/auth/auth.controller.js - localLogin

exports.localLogin = async (ctx) => {
    // 데이터 검증
    const schema = Joi.object().keys({
        email: Joi.string().email().required(),
        password: Joi.string().required()
    });

    const result = Joi.validate(ctx.request.body, schema);

    if(result.error) {
        ctx.status = 400; // Bad Request
        return;
    }
};

그 다음에는, 우선 findByEmail 을 통하여 게정을 찾고, 만약에 데이터가 없다면 에러를 응답하고, 존재한다면 비밀번호체크 과정을 수행합니다.

src/api/auth/auth.controller.js - localLogin

exports.localLogin = async (ctx) => {
    // 데이터 검증
    const schema = Joi.object().keys({
        email: Joi.string().email().required(),
        password: Joi.string().required()
    });

    const result = Joi.validate(ctx.request.body, schema);

    if(result.error) {
        ctx.status = 400; // Bad Request
        return;
    }

    const { email, password } = ctx.request.body; 

    let account = null;
    try {
        // 이메일로 계정 찾기
        account = await Account.findByEmail(email);
    } catch (e) {
        ctx.throw(500, e);
    }

    if(!account || !account.validatePassword(password)) {
    // 유저가 존재하지 않거나 || 비밀번호가 일치하지 않으면
        ctx.status = 403; // Forbidden
        return;
    }

    ctx.body = account.profile;
};

이제, 아까 만들었던 계정정보로 로그인 요청을 해보세요. 프로필이 제대로 응답되었다면, 잘못된 계정으로도 로그인을 시도해보세요.

요청

POST http://localhost:4000/api/auth/login/local
{
    "email": "public.velopert@gmail.com",
    "password": "123123"
}

응답

{
    "thumbnail": "/static/images/default_thumbnail.png",
    "username": "velopert"
}

아이디/이메일 중복확인 API 만들기

회원가입 API를 문제없이 만들었다면, 이 API는 쉽게 만들 수 있을 것 입니다.

src/api/auth/auth.controller.js - exists

exports.exists = async (ctx) => {
    const { key, value } = ctx.params;
    let account = null;

    try {
        // key 에 따라 findByEmail 혹은 findByUsername 을 실행합니다.
        account = await (key === 'email' ? Account.findByEmail(value) : Account.findByUsername(value));    
    } catch (e) {
        ctx.throw(500, e);
    }

    ctx.body = {
        exists: account !== null
    };
};

위 코드에서 주의 하실 점은, await 뒤에 () 로 감싸주어야 한 다는 것 입니다. 그렇게 하지 않으면 코드가 이상작동을 하게 됩니다.

API 를 작성하셨다면, 다음과 같이 테스트를 해보세요.

GET http://localhost:4000/api/auth/exists/email/public.velopert@gmail.com
GET http://localhost:4000/api/auth/exists/email/new.public.velopert@gmail.com
GET http://localhost:4000/api/auth/exists/username/velopert
GET http://localhost:4000/api/auth/exists/username/newvelopert

이제, 회원 인증을 위한 API 를 어느정도 준비했습니다. 사용자가 로그인을 했을 땐, 사용자가 로그인을 했다는것을 인지하기 위하여 인증정보를 관리 할 필요가 있습니다. 이에 대해선 다음 이어지는 4장에서 다루도록 하겠습니다.

results matching ""

    No results matching ""