Skip to content

TeamHappic/happic-Server

Folders and files

NameName
Last commit message
Last commit date
Jul 22, 2022
Jul 16, 2022
Aug 4, 2022
Jul 5, 2022
Jul 20, 2022
Jul 17, 2022
Jul 25, 2022
Jul 10, 2022
Jul 21, 2022
Jul 5, 2022
Jul 22, 2022
Jul 22, 2022
Jul 21, 2022
Jul 22, 2022

Repository files navigation


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                   
 β”— πŸ“œ index.ts

πŸ“¦ controllers               
 ┣ πŸ“œ index.ts

πŸ“¦ interfaces                
 β”— πŸ“‚ film
 ┃ β”— πŸ“œ FilmInfo.ts
 β”— πŸ“‚ keyword
   β”— πŸ“œ KeywordInfo.ts
 β”— πŸ“‚ user
   β”— πŸ“œ UserInfo.ts

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

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

πŸ“¦ models                    
 ┣ πŸ“œ Film.ts
 ┣ πŸ“œ Keyword.ts
 β”— πŸ“œ User.ts

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

πŸ“¦ routes                    
 ┣ πŸ“œ index.ts

πŸ“¦ services                  
 ┣ πŸ“œ 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

  • 개발 ν™˜κ²½ : Typescript, Express(Node.js)
  • λ°μ΄ν„°λ² μ΄μŠ€ : MongoDB, AWS S3
  • μ„œλ²„ ν™˜κ²½ : AWS EC2, PM2 180420764-1afac15d-1ef5-4c47-b68e-90cb128c3d7c