/happic-Server

Be happy, take a happic: πŸ“Έ

Primary LanguageTypeScript


happic: ν•΄ν”½

Be happy, take a happic:

맀일의 좔얡이 λ‚˜μ˜ 행볡이 λ˜μ–΄, ν•΄ν”½
μ €ν¬λŠ” ν•΄ν”½μž…λ‹ˆλ‹€

SOPT 30th APP JAM
ν”„λ‘œμ νŠΈ κΈ°κ°„ : 2022.07.02 ~ 2022.07.23

μΆ”ν›„ 릴리즈 μ˜ˆμ •

μ£Όμš” κΈ°λŠ₯ μ†Œκ°œ

KakaoTalk_20220722_234934351

KakaoTalk_20220722_234934351_01

KakaoTalk_20220722_234934351_02

KakaoTalk_20220722_234934351_03


Service workflow

IA-FLOW@3x

Team happic SERVER Developers

μ΄μ„œμš° κΉ€λ™μž¬ μœ μ†‘κ²½
dltjdn ehdwoKIM ssong915

🌲 Git Branch Convention

  • main: 배포λ₯Ό μœ„ν•œ 브랜치 ( μ΅œμ’…λ³Έ )

  • develop: κΈ°λŠ₯ 개발이 μ™„λ£Œλœ μ½”λ“œλ“€μ΄ λͺ¨μ΄λŠ” κ³³ ( κ²€μ¦λœ 곳이자 검증할 κ³³ )

  • feature: κΈ°λŠ₯ κ°œλ°œμ„ μœ„ν•œ 브랜치 ( feature/본인이름/κΈ°λŠ₯λͺ… )


πŸ’Œ Commit Convention

- [ADD] : μƒˆλ‘œμš΄ κΈ°λŠ₯ κ΅¬ν˜„
- [FEAT] : ADD μ΄μ™Έμ˜ λΆ€μˆ˜μ μΈ μ½”λ“œ μΆ”κ°€, 라이브러리 μΆ”κ°€, μƒˆλ‘œμš΄ 파일 생성 μ‹œ
- [CHORE]: μ½”λ“œ μˆ˜μ •, λ‚΄λΆ€ 파일 μˆ˜μ •
- [FIX] : 버그, 였λ₯˜ ν•΄κ²°
- [DEL] : μ“Έλͺ¨μ—†λŠ” μ½”λ“œ μ‚­μ œ
- [DOCS] : READMEλ‚˜ WIKI λ“±μ˜ λ¬Έμ„œ κ°œμ •
- [MOVE] : ν”„λ‘œμ νŠΈ λ‚΄ νŒŒμΌμ΄λ‚˜ μ½”λ“œμ˜ 이동
- [RENAME] : 파일 μ΄λ¦„μ˜ λ³€κ²½
- [STYLE] : μ½”λ“œκ°€ μ•„λ‹Œ μŠ€νƒ€μΌ 변경을 ν•˜λŠ” 경우

✨ Coding Convention

λͺ…λͺ…κ·œμΉ™ (Naming Conventions)
  1. μ΄λ¦„μœΌλ‘œλΆ€ν„° μ˜λ„κ°€ μ½ν˜€μ§ˆ 수 있게 μ“΄λ‹€.
  2. 였브젝트, ν•¨μˆ˜, 그리고 μΈμŠ€ν„΄μŠ€μ—λŠ” camelCaseλ₯Ό μ‚¬μš©ν•œλ‹€.
  3. ν΄λž˜μŠ€λ‚˜ constructorμ—λŠ” PascalCaseλ₯Ό μ‚¬μš©ν•œλ‹€.
  4. ν•¨μˆ˜ 이름은 동사 + λͺ…사 ν˜•νƒœλ‘œ μž‘μ„±ν•œλ‹€.
    ex) postUserInformation()
  5. μ•½μ–΄ μ‚¬μš©μ€ μ΅œλŒ€ν•œ μ§€μ–‘ν•œλ‹€.
  6. 이름에 λ„€ 단어 이상이 λ“€μ–΄κ°€λ©΄ νŒ€μ›κ³Ό μƒμ˜λ₯Ό 거친 ν›„ μ‚¬μš©ν•œλ‹€.
  7. λ°μ΄ν„°λ² μ΄μŠ€ λͺ…은 μ˜μ–΄ μ†Œλ¬Έμžλ‘œ κ΅¬μ„±ν•œλ‹€.
블둝 (Blocks)
  1. λ³΅μˆ˜ν–‰μ˜ λΈ”λ‘μ—λŠ” μ€‘κ΄„ν˜Έ({})λ₯Ό μ‚¬μš©ν•œλ‹€.
  2. λ³΅μˆ˜ν–‰ λΈ”λ‘μ˜ if 와 else λ₯Ό μ΄μš©ν•˜λŠ” 경우 else λŠ” if 블둝 끝의 μ€‘κ΄„ν˜Έ( } )와 같은 행에 μœ„μΉ˜μ‹œν‚¨λ‹€.
μ½”λ©˜νŠΈ (Comments)
  1. λ³΅μˆ˜ν˜•μ˜ μ½”λ©˜νŠΈλŠ” /** ... */ λ₯Ό μ‚¬μš©ν•œλ‹€.
  2. 단일 ν–‰μ˜ μ½”λ©˜νŠΈμ—λŠ” // 을 μ‚¬μš©ν•˜κ³  μ½”λ©˜νŠΈλ₯Ό μΆ”κ°€ν•˜κ³  싢은 μ½”λ“œμ˜ 상뢀에 λ°°μΉ˜ν•œλ‹€. 그리고 μ½”λ©˜νŠΈμ˜ μ•žμ— 빈 행을 λ„£λŠ”λ‹€.
λ¬Έμžμ—΄ (Strings)
  1. λ¬Έμžμ—΄μ—λŠ” μ‹±ν¬μΏΌνŠΈ '' λ₯Ό μ‚¬μš©ν•œλ‹€..
  2. ν”„λ‘œκ·Έλž¨μ—μ„œ λ¬Έμžμ—΄μ„ μƒμ„±ν•˜λŠ” κ²½μš°λŠ” λ¬Έμžμ—΄ 연결이 μ•„λ‹Œ template stringsλ₯Ό μ΄μš©ν•œλ‹€.
ν•¨μˆ˜ (Functions)
  1. ν™”μ‚΄ν‘œ ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•œλ‹€.
 var arr1 = [1, 2, 3];
 var pow1 = arr.map(function (x) { // ES5 Not Good
   return x * x;
 });

 const arr2 = [1, 2, 3];
 const pow2 = arr.map(x => x * x); // ES6 Good
  1. 비동기 ν•¨μˆ˜ μ‚¬μš© μ‹œ Promiseν•¨μˆ˜μ˜ μ‚¬μš©μ€ μ§€μ–‘ν•˜κ³  async, await λ₯Ό μ‚¬μš©ν•˜λ„λ‘ ν•œλ‹€.
쑰건식과 등가식 (Comparsion Operators & Equality)
  1. ==μ΄λ‚˜ !=보닀 === 와 !==을 μ‚¬μš©ν•œλ‹€.
  2. λ‹¨μΆ•ν˜•μ„ μ‚¬μš©ν•œλ‹€.
  3. 비동기 ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•  λ•Œ Promiseν•¨μˆ˜μ˜ μ‚¬μš©μ€ μ§€μ–‘ν•˜κ³  async, awaitλ₯Ό 쓰도둝 ν•œλ‹€.

πŸ—‚ Project Foldering

πŸ“¦ config                    // port, mongoURI λ“± μ„€μ •
 β”— πŸ“œ index.ts

πŸ“¦ controllers               // serviceμ—μ„œ 처리된 λ‘œμ§λ“€μ„ 전달 λ°›μ•„ responseν•΄μ€Œ
 ┣ πŸ“œ index.ts

πŸ“¦ interfaces                // type interface μ •μ˜
 β”— πŸ“‚ film
 ┃ β”— πŸ“œ FilmInfo.ts
 β”— πŸ“‚ keyword
   β”— πŸ“œ KeywordInfo.ts
 β”— πŸ“‚ user
   β”— πŸ“œ UserInfo.ts

πŸ“¦ loaders
 β”— πŸ“œ db.ts

πŸ“¦ middlewares
 β”— πŸ“œ auth.ts

πŸ“¦ models                    // mongoose.Schema μ •μ˜
 ┣ πŸ“œ Film.ts
 ┣ πŸ“œ Keyword.ts
 β”— πŸ“œ User.ts

πŸ“¦ modules
 β”— πŸ“œ util.ts
 β”— πŸ“œ statusCode.ts
 β”— πŸ“œ responseMessage.ts

πŸ“¦ routes                    // endpoint μ •μ˜
 ┣ πŸ“œ index.ts

πŸ“¦ services                  // 상세 κ΅¬ν˜„, controller둜 전달 됨
 ┣ πŸ“œ index.ts

πŸ—’ DB Schema

Char
const CharSchema = new mongoose.Schema({
  characterId: {
    type: Number,
    required: true,
  },
  characterName: {
    type: String,
    required: true,
  },
});
File
const FileSchema = new mongoose.Schema(
  {
    link: {
      type: String,
      required: true,
    },
    fileName: {
      type: String,
      required: true,
    },
  },
  {
    timestamps: true, // createdAt, updatedAt μžλ™κΈ°λ‘
  }
);
User
const UserSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true,
  },
  social: {
    type: String,
    required: true,
    unique: true,
  },
  socialId: {
    type: String,
    required: true,
    unique: true,
  },
  email: {
    type: String,
    required: true,
    unique: true,
  },
  characterId: {
    type: Number,
    required: true,
  },
  characterName: {
    type: String,
    required: true,
  },
  growthRate: {
    type: Number,
    required: true,
    default: 0,
  },
  level: {
    type: Number,
    required: true,
    default: 1,
  },
  film: [
    {
      type: mongoose.Types.ObjectId,
      ref: 'Film',
    },
  ],
  count: {
    type: Number,
    required: true,
    default: 0,
  },
  fcmToken: {
    type: String,
    required: true,
    unique: true,
  },
  refreshToken: {
    type: String,
    required: true,
    unique: true,
  },
});
Film
const FilmSchema = new mongoose.Schema(
  {
    writer: {
      type: mongoose.Types.ObjectId,
      required: true,
      ref: 'User',
    },
    photo: {
      type: String,
      required: true,
    },
    thumbnail: {
      type: String,
    },
    keyword: [
      {
        type: mongoose.Types.ObjectId,
        required: true,
        ref: 'Keyword',
      },
    ],
    year: {
      type: Number,
      required: true,
    },
    month: {
      type: Number,
      required: true,
    },
  },
  {
    timestamps: true, // createdAt, updatedAt μžλ™κΈ°λ‘
  }
);
Keyword
const KeywordSchema = new mongoose.Schema(
  {
    writer: {
      type: mongoose.Types.ObjectId,
      required: true,
      ref: 'User',
    },
    category: {
      type: String,
      required: true,
    },
    content: {
      type: String,
      required: true,
    },
    year: {
      type: Number,
      required: true,
    },
    month: {
      type: Number,
      required: true,
    },
    count: {
      type: Number,
      required: true,
      default: 0,
    },
  },
  {
    timestamps: true, // createdAt, updatedAt μžλ™κΈ°λ‘
  }
);

πŸ›  API


πŸ›  Dependencies module (package.json)

{
  "name": "node-typescript-init",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "nodemon",
    "build": "tsc",
    "start:dev": "node dist/index.js",
    "test": "mocha -r ts-node/register src/test/daily.spec.ts"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/chai": "^4.3.1",
    "@types/express": "^4.17.13",
    "@types/jsonwebtoken": "^8.5.8",
    "@types/mocha": "^9.1.1",
    "@types/mongoose": "^5.11.97",
    "@types/multer": "^1.4.7",
    "@types/multer-s3": "^2.7.12",
    "@types/node": "^17.0.25",
    "@types/supertest": "^2.0.12",
    "@typescript-eslint/eslint-plugin": "^5.30.5",
    "@typescript-eslint/parser": "^5.30.5",
    "chai": "^4.3.6",
    "eslint": "^8.19.0",
    "mocha": "^10.0.0",
    "nodemon": "^2.0.15",
    "prettier": "^2.7.1",
    "supertest": "^6.2.4",
    "ts-node": "^10.7.0",
    "typescript": "^4.6.3"
  },
  "dependencies": {
    "aws-sdk": "^2.1143.0",
    "axios": "^0.27.2",
    "bcryptjs": "^2.4.3",
    "bucket": "^0.0.1",
    "dayjs": "^1.11.3",
    "dotenv": "^16.0.0",
    "eslint-config-prettier": "^8.5.0",
    "eslint-plugin-prettier": "^4.2.1",
    "express": "^4.18.1",
    "express-validator": "^6.14.2",
    "firebase-admin": "^11.0.0",
    "jsonwebtoken": "^8.5.1",
    "mongoose": "^6.3.1",
    "multer": "^1.4.4",
    "multer-s3": "^2.10.0",
    "node-schedule": "^2.1.0",
    "winston": "^3.8.1"
  }
}

πŸ›  Server Architecture

180420764-1afac15d-1ef5-4c47-b68e-90cb128c3d7c


πŸ“š Task & Role

Route κΈ°λŠ₯ κ΅¬ν˜„ μ—¬λΆ€ λ‹΄λ‹Ήμž
home 메인화면 쑰회 μœ μ†‘κ²½
  ν‘Έμ‹œμ•Œλ¦Ό 쑰회 κΉ€λ™μž¬
  ν•΄ν”½μΊ‘μŠ 쑰회 κΉ€λ™μž¬
character 캐릭터 이름 μž…λ ₯ κΉ€λ™μž¬
mypage ν•΄ν”½λ ˆν¬νŠΈ 쑰회 μœ μ†‘κ²½
  ν‚€μ›Œλ“œ 전체 μˆœμœ„ 쑰회 μœ μ†‘κ²½
  μΉ΄ν…Œκ³ λ¦¬λ³„ 전체 μˆœμœ„ 쑰회 μœ μ†‘κ²½
  월별 ν•˜λ£¨ν•΄ν”½ 횟수 쑰회 μœ μ†‘κ²½
daily ν•˜λ£¨ν•΄ν”½ 전체 쑰회 μ΄μ„œμš°
  ν•˜λ£¨ν•΄ν”½ 생성 μ΄μ„œμš°
  ν•˜λ£¨ν•΄ν”½ ν‚€μ›Œλ“œ 쑰회 μ΄μ„œμš°
  ν•˜λ£¨ν•΄ν”½ 상세 쑰회 μ΄μ„œμš°
  ν•˜λ£¨ν•΄ν”½ μ‚­μ œ μ΄μ„œμš°
  ν•˜λ£¨μ œλͺ© 전체 쑰회 μœ μ†‘κ²½
user μ†Œμ…œλ‘œκ·ΈμΈ κΉ€λ™μž¬
setting 캐릭터 λ³€κ²½ κΉ€λ™μž¬