• 아파트 단지를 검색하면 단지 주소, 좌표가 있었으면 좋겠다는 생각에 지도 관련 api를 제공하는 서비스를 찾아봤다.
  • NaverAPI와 kakaoAPI가 있었는데 쉽게 사용하는것은 kakaoAPI가 훠얼씬 편했다.
  • 네이버의 경우 NBP로 지도API가 넘어가면서 결재카드를 등록하는 등의 번거로운 작업이 굉장히 많다.
  • 반면 카카오는 사용하는게 매~우 편리했다. RestAPI 툴도 제공해서 바로 결과값도 확인이 가능
  • 네이버도 가능했을지 모르지만... 일단 회원가입하고 카드등록하라고해서 일단 패스
  • 나중에 정말 사용을 해야하는데 카카오에 기능이 없거나 요청에 제한이 생긴다면 네이버를 사용할지 몰라도 당장은....
  • 일단 네이버는 일에 요청수 제한이 있는데 카카오는 별 다른... 가이드가 없었다.
  • 네이버 지도 API
  • 카카오 지도 API

카카오 지도에서는 간단하게 테스트가 가능한데 아래에 키만 입력하면 조회가 가능하다

{
    "documents": [
        {
            "address_name": "경기 성남시 분당구 삼평동 681",
            "category_group_code": "",
            "category_group_name": "",
            "category_name": "서비스,산업 > 인터넷,IT > 포털서비스",
            "distance": "",
            "id": "18577297",
            "phone": "1899-1326",
            "place_name": "카카오 판교오피스",
            "place_url": "http://place.map.kakao.com/18577297",
            "road_address_name": "경기 성남시 분당구 판교역로 235",
            "x": "127.108212226945",
            "y": "37.4020560436306"
        },
        {
            "address_name": "제주특별자치도 제주시 영평동 2181",
            "category_group_code": "",
            "category_group_name": "",
            "category_name": "서비스,산업 > 인터넷,IT > 포털서비스",
            "distance": "",
            "id": "18059921",
            "phone": "1899-1326",
            "place_name": "카카오 스페이스닷원",
            "place_url": "http://place.map.kakao.com/18059921",
            "road_address_name": "제주특별자치도 제주시 첨단로 242",
            "x": "126.57066132748933",
            "y": "33.450677320070916"
        },
        {
            "address_name": "제주특별자치도 제주시 영평동 2184",
            "category_group_code": "",
            "category_group_name": "",
            "category_name": "서비스,산업 > 인터넷,IT > 포털서비스",
            "distance": "",
            "id": "22251293",
            "phone": "1899-1326",
            "place_name": "카카오 스페이스닷투",
            "place_url": "http://place.map.kakao.com/22251293",
            "road_address_name": "제주특별자치도 제주시 첨단로 216-19",
            "x": "126.570875463183",
            "y": "33.4526219140826"
        },
        {
            "address_name": "경기 성남시 분당구 백현동 541",
            "category_group_code": "",
            "category_group_name": "",
            "category_name": "가정,생활 > 문구,사무용품 > 디자인문구 > 카카오프렌즈",
            "distance": "",
            "id": "26944943",
            "phone": "031-5170-2451",
            "place_name": "카카오프렌즈 현대백화점판교점",
            "place_url": "http://place.map.kakao.com/26944943",
            "road_address_name": "경기 성남시 분당구 판교역로146번길 20",
            "x": "127.112385409653",
            "y": "37.392202184912"
        },
        {
            "address_name": "경기 성남시 분당구 삼평동 680",
            "category_group_code": "",
            "category_group_name": "",
            "category_name": "금융,보험 > 금융서비스 > 은행",
            "distance": "",
            "id": "27578089",
            "phone": "1599-3333",
            "place_name": "한국카카오은행",
            "place_url": "http://place.map.kakao.com/27578089",
            "road_address_name": "경기 성남시 분당구 판교역로 231",
            "x": "127.108644810967",
            "y": "37.4012843732024"
        },
        {
            "address_name": "경기 성남시 분당구 백현동 537",
            "category_group_code": "",
            "category_group_name": "",
            "category_name": "금융,보험 > 금융서비스",
            "distance": "",
            "id": "949394317",
            "phone": "1644-7405",
            "place_name": "카카오페이",
            "place_url": "http://place.map.kakao.com/949394317",
            "road_address_name": "경기 성남시 분당구 판교역로 152",
            "x": "127.110699049301",
            "y": "37.39426531392528"
        },
        {
            "address_name": "경기 성남시 분당구 삼평동 685",
            "category_group_code": "",
            "category_group_name": "",
            "category_name": "서비스,산업 > 전문대행 > 고객관리,TM",
            "distance": "",
            "id": "25824400",
            "phone": "",
            "place_name": "카카오 고객센터",
            "place_url": "http://place.map.kakao.com/25824400",
            "road_address_name": "경기 성남시 분당구 판교역로241번길 20",
            "x": "127.107372945892",
            "y": "37.4027632089203"
        },
        {
            "address_name": "경기 성남시 분당구 삼평동 672",
            "category_group_code": "",
            "category_group_name": "",
            "category_name": "서비스,산업 > 인터넷,IT > 소프트웨어",
            "distance": "",
            "id": "13556208",
            "phone": "1644-4755",
            "place_name": "카카오페이지",
            "place_url": "http://place.map.kakao.com/13556208",
            "road_address_name": "경기 성남시 분당구 판교역로 221",
            "x": "127.108996408808",
            "y": "37.4007470412071"
        },
        {
            "address_name": "경기 성남시 분당구 삼평동 633",
            "category_group_code": "",
            "category_group_name": "",
            "category_name": "스포츠,레저 > 골프",
            "distance": "",
            "id": "18858127",
            "phone": "1666-1538",
            "place_name": "카카오VX",
            "place_url": "http://place.map.kakao.com/18858127",
            "road_address_name": "경기 성남시 분당구 판교로228번길 17",
            "x": "127.10158226559571",
            "y": "37.400195010191624"
        },
        {
            "address_name": "경기 성남시 분당구 백현동 537",
            "category_group_code": "",
            "category_group_name": "",
            "category_name": "서비스,산업 > 기업",
            "distance": "",
            "id": "547176330",
            "phone": "",
            "place_name": "카카오모빌리티",
            "place_url": "http://place.map.kakao.com/547176330",
            "road_address_name": "경기 성남시 분당구 판교역로 152",
            "x": "127.1101250888609",
            "y": "37.39407843730005"
        },
        {
            "address_name": "경기 성남시 분당구 백현동 537",
            "category_group_code": "",
            "category_group_name": "",
            "category_name": "서비스,산업 > 인터넷,IT > 게임업체",
            "distance": "",
            "id": "24974641",
            "phone": "1566-8834",
            "place_name": "카카오게임즈",
            "place_url": "http://place.map.kakao.com/24974641",
            "road_address_name": "경기 성남시 분당구 판교역로 152",
            "x": "127.11016618280905",
            "y": "37.39437843929819"
        },
        {
            "address_name": "경기 성남시 분당구 삼평동 680",
            "category_group_code": "PS3",
            "category_group_name": "어린이집,유치원",
            "category_name": "교육,학문 > 유아교육 > 어린이집",
            "distance": "",
            "id": "26914389",
            "phone": "031-607-9993",
            "place_name": "늘예솔카카오판교어린이집",
            "place_url": "http://place.map.kakao.com/26914389",
            "road_address_name": "경기 성남시 분당구 판교역로 231",
            "x": "127.108879816692",
            "y": "37.401345426449"
        },
        {
            "address_name": "경기 성남시 분당구 삼평동 681",
            "category_group_code": "",
            "category_group_name": "",
            "category_name": "스포츠,레저 > 골프 > 골프연습장 > 스크린골프연습장",
            "distance": "",
            "id": "245183397",
            "phone": "031-778-6806",
            "place_name": "카카오VX 판교",
            "place_url": "http://place.map.kakao.com/245183397",
            "road_address_name": "경기 성남시 분당구 판교역로 235",
            "x": "127.108645940077",
            "y": "37.4020664581538"
        },
        {
            "address_name": "경기 성남시 분당구 삼평동 670",
            "category_group_code": "",
            "category_group_name": "",
            "category_name": "서비스,산업 > 기업",
            "distance": "",
            "id": "730664075",
            "phone": "",
            "place_name": "카카오커머스",
            "place_url": "http://place.map.kakao.com/730664075",
            "road_address_name": "경기 성남시 분당구 대왕판교로 660",
            "x": "127.10681400191869",
            "y": "37.40046431022458"
        },
        {
            "address_name": "경기 성남시 분당구 삼평동 681",
            "category_group_code": "",
            "category_group_name": "",
            "category_name": "교통,수송 > 자동차 > 전기자동차 충전소",
            "distance": "",
            "id": "679200161",
            "phone": "",
            "place_name": "판교H스퀘어 카카오판교사옥 전기차충전소",
            "place_url": "http://place.map.kakao.com/679200161",
            "road_address_name": "경기 성남시 분당구 판교역로 235",
            "x": "127.108616558231",
            "y": "37.4020547718792"
        }
    ],
    "meta": {
        "is_end": false,
        "pageable_count": 45,
        "same_name": {
            "keyword": "카카오",
            "region": [],
            "selected_region": ""
        },
        "total_count": 421
    }
}
  • 포스트 목록을 볼때 한 페이지에 보이는 포스트의 개수는 적당히 보여줘야 한다.
  • 전체 포스트를 가져온다면 network I/O가 상당할것이다
  • 포스트 내용도 동일하게 전부가 아닌 일부만 보여주는게 I/O를 줄이는데 도움이 될것!
export const list = async ctx => {
  const page = parseInt(ctx.query.page | '1', 10);

  if (page < 1) {
    ctx.status = 400;
    return;
  }

  try {
    const posts = await Post.find()
      .sort({ _id: -1 })
      .limit(10)
      .skip((page - 1) * 10)
      .exec();
    const postCount = await Post.countDocuments().exec();
    ctx.set('Last-Page', Math.ceil(postCount / 10));
    ctx.body = posts
  • 위에 sort는 최근에 작성한 글을 먼저 오도록
  • limit은 10개의 post만 가져오도록
  • skip은 pagination을 할때 사용
    • skip(10)은 10개 뒤로 이동
  • Last-Page의 값은 Header에 함께 포함

body에 대한 처리

    ctx.body = posts
      .map(post => post.toJSON())
      .map(post => ({
        ...post,
        body:
          post.body.length < 200 ? post.body : `${post.body.slice(0, 200)}...`,
      }));
  • 결과로 가져온 body에서 ㅅ길이를 조절하기 위해서는 다음과 같이 body를 가져와 줄이면 된다.
  • 위 결과는 서버쪽에서 처리하는 내용, db에서도 처리가 가능할까?
import Post from '../../models/post';

/* 포스트 작성
POST /api/posts
{ title, body }
*/
export const write = async ctx => {
  const { title, body, tags } = ctx.request.body;
  const post = new Post({
    title,
    body,
    tags,
  });
  try {
    await post.save();
    ctx.body = post;
  } catch (e) {
    ctx.throw(500, e);
  }
};

/* 포스트 목록 조회
GET /api/posts
*/
export const list = async ctx => {
  try {
    const posts = await Post.find().exec();
    ctx.body = posts;
  } catch (e) {
    ctx.throw(500, e);
  }
};

/* 특정 포스트 조회
GET /api/posts/:id
*/

export const read = async ctx => {
  const { id } = ctx.params;
  try {
    const post = await Post.findById(id).exec();
    if (!post) {
      ctx.status = 404; // Not Found
      return;
    }
    ctx.body = post;
  } catch (e) {
    ctx.throw(500, e);
  }
};

/* 특정 포스트 제거
DELETE /api/posts/:id
*/
export const remove = async ctx => {
  const { id } = ctx.params;
  try {
    await Post.findByIdAndRemove(id).exec();
    ctx.status = 204; // No Content
  } catch (e) {
    ctx.throw(500, e);
  }
};

/* 포스트 수정 (특정 필드 변경)
PATCH /api/posts/:id
{ title, body }
*/
export const update = async ctx => {
  const { id } = ctx.params;
  try {
    const post = await Post.findByIdAndUpdate(id, ctx.request.body, {
      // false: 업데이트 이전의 데이터 반환
      // true:  업데이트된 데이터를 반환
      new: true,
    }).exec();
    if (!post) {
      ctx.status = 404;
      return;
    }
    ctx.body = post;
  } catch (e) {
    ctx.throw(500, e);
  }
}; 
  • 각 라우터에서 처리해야하는 함수 코드가 길면 유지보수가 어려우니
  • 라우트에서 처리하는 함수들만 모아서 컨트롤러(controller)를 만들자
# yarn add koa-bodyparser

src/api/posts/posts.ctrl.js

let postId = 1; // init

// posts data

const posts = [
  {
    id: 1,
    title: '제목',
    body: '내용',
  },
];

/* 포스트 작성
POST /api/posts
{ title, body }
*/
exports.write = ctx => {
  const { title, body } = ctx.request.body;
  postId += 1;
  const post = { id: postId, title, body };
  posts.push(post);
  ctx.body = post;
};

/* 포스트 목록 조회
GET /api/posts
*/
exports.list = ctx => {
  ctx.body = posts;
};

/* 특정 포스트 조회
GET /api/posts/:id
*/

exports.read = ctx => {
  const { id } = ctx.params; // default datatype: string
  const post = posts.find(p => p.id.toString() === id);
  if (!post) {
    ctx.status = 404;
    ctx.body = {
      message: '포스트가 존재하지 않습니다',
    };
    return;
  }
  ctx.body = post;
};

/* 특정 포스트 제거
DELETE /api/posts/:id
*/
exports.remove = ctx => {
  const { id } = ctx.params;
  const index = posts.findIndex(p => p.id.toString() === id);

  if (index === -1) {
    ctx.status = 404;
    ctx.body = {
      message: '포스트가 존재하지 않습니다',
    };
    return;
  }
  posts.splice(index, 1);
  ctx.status = 204; // No Content
};

/* 포스트 수정 (교체)
PUT /api/posts/:id
{ title, body }
*/
exports.replace = ctx => {
  const { id } = ctx.params;
  const index = posts.findIndex(p => p.id.toString() === id);
  if (index === -1) {
    ctx.status = 404;
    ctx.body = {
      message: '포스트가 존재하지 않습니다',
    };
    return;
  }
  posts[index] = {
    id,
    ...ctx.request.body,
  };
  ctx.body = posts[index];
};

/* 포스트 수정 (특정 필드 변경)
PATCH /api/posts/:id
{ title, body }
*/
exports.update = ctx => {
  const { id } = ctx.params;
  const index = posts.findIndex(p => p.id.toString() === id);
  if (index === -1) {
    ctx.status = 404;
    ctx.body = {
      message: '포스트가 존재하지 않습니다',
    };
    return;
  }
  posts[index] = {
    ...posts[index],
    ...ctx.request.body,
  };
  ctx.body = posts[index];
};

src/api/posts/index.js

const Router = require('koa-router');
const postsCtrl = require('./posts.ctrl');

const posts = new Router(); 

posts.get('/', postsCtrl.list);
posts.post('/', postsCtrl.write);
posts.get('/:id', postsCtrl.read);
posts.delete('/:id', postsCtrl.remove);
posts.put('/:id', postsCtrl.replace);
posts.patch('/:id', postsCtrl.update);

module.exports = posts; 

+ Recent posts