SNS 백엔드 서비스 구현
📌 본 서비스는 SNS(Social Network Service)이다.
📌 사용자는 본 서비스에 접속하여, 게시물을 업로드하거나 다른 사람의 게시물을 확인하고, 좋아요 수를 누를 수 있다.
개발 기간: 7.20(수) ~ 7.26(화) + a
-
유저 관리
📍 유저 회원가입
➡️ 이메일을 ID로 사용
POST /users/signup
// request { "userId": "tester1@naver.com", "password": "qwer1234", "userName": "tester1" }
// response { "message": "회원가입이 완료됐습니다!", }
📍 유저 로그인 및 인증
➡️ JWT 토큰을 발급받으며, 이를 추후 사용자 인증으로 사용
POST /users/signin
// request { "userId": "tester1@naver.com", "password": "qwer1234", }
// response { "message": "로그인이 완료됐습니다!", } // JWT Authorization: "Bearer dskfjiowenv12s@$4"
-
게시글
📍 게시글 생성
➡️ 제목, 내용, 해시태그 등을 입력하여 생성 (제목, 내용, 해시태그는 필수 입력사항)
➡️ 작성자 정보는 request body에 존재하지 않고, 해당 API를 요청한 인증정보에서 추출하여 등록 (API단에서 토큰에서 얻은 사용자 정보를 게시글 생성 때 작성자로 넣어 사용)
➡️ 해시태그는 #로 시작되고 ,로 구분되는 텍스트가 입력됨 ex) {”hashtags”, “#맛집,#서울,#브런치 카페,#주말”, …}
POST /tweets/create
// request { "title": "제목", "contents": "내용", "해시태그": "['해시태그1','해시태그2', ...]", } // 토큰에서 얻은 사용자 정보에서 추출하여 작성자 정보 따로 등록
// response { "message": "게시글이 생성됐습니다!" }
📍 게시글 수정
➡️ 작성자만 수정 가능
PATCH /tweets/update/:tweetNum
// request params: tweetNum { "title": "제목", "contents": "내용", "해시태그": "["해시태그1","해시태그2", ...]", }
// response { "message": "게시글이 수정됐습니다!" }
📍 게시글 삭제
➡️ 작성자만 삭제 가능
➡️ 작성자는 삭제 게시글 복구 가능
PATCH /tweets/delete/:tweetNum - 삭제
// request params: tweetNum { }
// response { "message": "게시글이 삭제됐습니다!" }
PATCH /tweets/revoke/:tweetNum - 복구
// request params: tweetNum { }
// response { "message": "게시글이 복구됐습니다!", }
📍 게시글 상세보기
➡️ 모든 사용자는 모든 게시물 보기 가능
➡️ 작성자를 포함한 사용자는 본 게시물에 좋아요 누르기 가능 (좋아요 다시 누르면 좋아요 취소)
➡️ 작성자 포함한 사용자가 게시글을 상세보기 하면 조회수 1 증가 (회수제한 x)
GET /tweets/:tweetNum
// request params: tweetNum { }
// response { "tweetId": 20, "title": "제목!!", "contents": "내용!!", "views": 0, "agrees": 0, "deleted": false, "createdAt": "2022-07-21T05:25:46.000Z", "updatedAt": "2022-07-21T07:28:19.000Z", "userId": 1 }
📍 게시글 목록
➡️ 모든 사용자는 모든 게시물 보기 가능
GET /tweets
// request { }
// response { tweetsList: [ {tweetId, userId, title, contents, views, agrees, deleted, createdAt, updatedAt}, ... ] }
➡️ 게시글 목록에는 제목, 작성자, 해시태그, 작성일, 좋아요 수, 조회수 포함
➡️ Ordering (= Sorting, 정렬)
☑️ 사용자는 게시글 목록을 원하는 값으로 정렬 가능 (default: 작성일 & 작성일, 좋아요 수, 조회수 중 1개만 선택 가능) ☑️ 오름차 순, 내림차 순 선택 가능
➡️ Searching (= 검색)
☑️ 사용자는 입력한 키워드로 해당 키워드를 포함한 게시물 조회 가능
☑️ # Like 검색, 해당 키워드가 문자열 중 포함된 데이터 검색
☑️ 검색 방법1. some-url?search=후기 → “후기”가 제목에 포함된 게시글 목록
➡️ Filtering (= 필터링)
☑️ 사용자는 지정한 키워드로 해당 키워드를 포함한 게시물 필터링 가능
☑️ 예시1. some-url?hashtags=서울 → “서울” 해시태그를 가진 게시글 목록
☑️ 예시2. some-url?hashtags=서울,맛집 → “서울"과 “맛집" 해시태그를 모두 가진 게시글 목록
➡️ Pagination (= 페이지 기능)
☑️ 사용자는 1페이지 당 게시글 수 조정 가능 (default: 10건)
Method | route | |
---|---|---|
회원가입 | POST | /users/signup |
로그인 | POST | /users/signin |
마이페이지 | GET | /users/me |
게시글 작성 | POST | /tweets/create |
게시글 수정 | PATCH | /tweets/update/:tweetId |
게시글 삭제 | PATCH | /tweets/delete/:tweetId |
삭제 게시글 복구 | PATCH | /tweets/revoke/:tweetId |
상세 게시글 조회 | GET | /tweets/:tweetId |
게시글 리스트 조회 | GET | /tweets |
게시글 sorting | GET | /tweets?sortby=a&orderby=desc |
게시글 searching | GET | /tweets?search=a |
게시글 filtering | GET | /tweets?hashTags=a,b |
게시글 pagination | GET | /tweets?pageNum=1&limit=1 |
-
유저 테이블
☑️ id -
PK
,auto increment
☑️ userId - 사용자 이메일
☑️ password - 사용자 비밀번호
☑️ userName - 사용자 이름
☑️ nickName - 사용자 닉넴임
☑️ createdAt - 생성날짜 (
default Date()
) -
게시글 테이블
☑️ tweetId -
PK
,auto increment
☑️ userId -
FK
(유저 테이블_id)☑️ title - 제목
☑️ contents - 내용
☑️ views - 조회수
☑️ agrees - 좋아요 수
☑️ deleted - 존재 상태 (게시글 생성시
default false
, true: 삭제, false: 존재)☑️ createdAt - 생성날짜 (
default Date()
)☑️ updatedAt - 수정날짜 (게시글 생성시
null
,default Date()
) -
해시태그
☑️ hashTagId -
PK
,auto increment
☑️ tweetId -
FK
(게시글 테이블_tweetId)☑️ hashTags - 해시태그 (내용 리스트 형태로)
✔︎ Sequelize
, 즉 ORM을 이용하여 DB를 연동했다.
// db/database.js
import mysql from 'mysql2';
import { config } from '../config.js';
import SQ from 'sequelize';
/**
* Sequelize 연동하기
*/
const { host, user, database, password } = config.db;
export const sequelize = new SQ.Sequelize(database, user, password, {
host,
dialect: 'mysql',
logging: false,
});
// data/users.js
import SQ from 'sequelize';
import { sequelize } from '../db/database.js';
const DataTypes = SQ.DataTypes;
export const User = sequelize.define('user', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
userId: {
type: DataTypes.STRING(128),
allowNull: false,
},
password: {
type: DataTypes.STRING(128),
allowNull: false,
},
userName: {
type: DataTypes.STRING(128),
allowNull: false,
},
nickName: {
type: DataTypes.STRING(128),
allowNull: false,
},
});
// data/tweets.js
import SQ from 'sequelize';
import { sequelize } from '../db/database.js';
import { User } from './users.js';
import { HashTag } from './hashTags.js';
const DataTypes = SQ.DataTypes;
const Sequelize = SQ.Sequelize;
export const Tweet = sequelize.define('tweet', {
tweetId: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
title: {
type: DataTypes.STRING(1024),
allowNull: false,
},
contents: {
type: DataTypes.TEXT,
allowNull: false,
},
views: {
type: DataTypes.INTEGER,
defaultValue: 0,
allowNull: false,
},
agrees: {
type: DataTypes.INTEGER,
defaultValue: 0,
allowNull: false,
},
deleted: { // 삭제 여부 -> false: 존재, true: 삭제
type: DataTypes.BOOLEAN,
defaultValue: false,
allowNull: false,
},
});
User.hasMany(Tweet, {foreignKey: 'userId'});
Tweet.belongsTo(User, {foreignKey: 'userId'});
Tweet.belongsToMany(HashTag, {as: 'hashtags', through: 'tweets_hashtags', foreignKey: 'tweetId'});
HashTag.belongsToMany(Tweet, {as: 'tweets', through: 'tweets_hashtags', foreignKey: 'hashTagId'});
// data/hashTags.js
import SQ from 'sequelize';
import { sequelize } from '../db/database.js';
const DataTypes = SQ.DataTypes;
const Sequelize = SQ.Sequelize;
export const HashTag = sequelize.define('hashtag', {
hashTagId: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
hashTags: {
type: DataTypes.STRING(128),
allowNull: true,
}
});