IMMERSIVE #21 - 4WEEK REVIEW

GunW·2019년 10월 14일
1

짧기도 길기도 했던 4주 프로젝트가 종료되었습니다.

총 4주 중 3주를 백엔드를 담당하고, 1주를 프론트에 합류하였습니다.

그 간의 작업을 간단히 리뷰해보려 합니다! 😎


1. TOPIC

HOLLYSHIP

자신의 감정을 기반으로 글을 쓰고, 글을 보고 사용자들이 노래를 추천해주는 SNS입니다.

프론트는 React-native-ui-kitten 래핑하기, expo-av 노래재생 등 간단한 동작만 하였습니다.

React-native-ui-kitten은 Material-ui 같은 UI를 꾸미기 위한 라이브러리로 생각하시면 됩니다.

백엔드 작업을 하면서 프론트를 구성하다보니, 백엔드로 구성한 것들의 허점들이 많이 보였고,

그런 점들을 짚어가면서 백엔드를 살펴보겠습니다.


1-1. Agile Scrum

2주 프로젝트에 이어 이번 프로젝트로 Notion을 활용한 애자일 스크럼 방식으로 제작하였습니다.

팀 룰, 일정을 정하고 난이도 설정, 스프린트 설정 등을 모두 정해놓고 프로젝트를 진행하였습니다.

아래는 Sprint 설정 및 TaskCard 설정 사진입니다.

[ Sprint 설정 ]

[ TaskCard 설정 ]


2. Stack

express / JWT / MySQL / passport / sequelize-typescript / AWS EC2 / RDS / S3

타입스크립트는 잘 모르더라도 한 번 도입해봐야할 듯 하여서 추가하여

이번 프로젝트에서는 큰 활용을 하지 못하였습니다.

추후 계속 공부하며 익숙해지려합니다.

서버는 express 기반이고, sequelize로 SQL을 구성하였습니다.

배포는 EC2, MySQL은 RDS로, S3는 multer를 이용해 이미지 업로드에 사용하였습니다.


3. Schema Design

약간 수정되었지만 최종 스키마와 거의 일치합니다.

유저는 대부분의 테이블과 관계가 되어있으며, 다대다 관계로 구현을 많이 하게 되었습니다.

UserMusicsLike, MusicPlayList, UserPostLike, UserComment는 다대다 관계에 의해

사이에 관계를 짓기 위한 테이블입니다.

초기에 디자인을 제대로 구성하지 못하여 프로젝트를 시작할 때 상당히 힘이 들었습니다.

SQL에 대해 제대로 공부를 하고, 스키마부터 제대로 구성하는 것을 권장합니다.


4. sequelize-typescript

sequelize를 이용하면 JS구문으로 SQL을 구현할 수 있습니다.

package.json의 scripts를 다음과 같이 사용하였습니다.

yarn start로 한번에 빌드, 배포, pm2 까지 시작합니다.

"scripts": {
    "start": "tsc -p . && NODE_ENV=production pm2 start dist/server.js",
}

물론 .env 설정, config.json 설정도 따로 해주어야 하며, 다음과 같습니다.

// config/config.json
{
  "development": {
    "username": "사용자 (root)",
    "password": "사용자 비밀번호",
    "database": "데이터베이스 이름",
    "host": "보통 로컬호스트 (127.0.0.1)",
    "dialect": "mysql"
  },
  "production": {
    "username": "사용자",
    "password": "비밀번호",
    "database": "데이터베이스 이름",
    "host": "EC2 서버 호스트",
    "dialect": "mysql",
    "pool": {
      "maxConnections": 5,
      "maxIdleTime": 30
    }
  }
}

마지막 production의 pool은 서버가 느려졌을 때 연결 시간을 조금 늘리는 것입니다.

굳이 필요하지 않으며, 저는 사용 도중 몇몇 오류 때문에 추가하였었습니다.

PORT=포트이름
NODE_ENV=production (개발모드 일경우 development)
COOKIE_SECRET=쿠키 시크릿키
JWT_SECRET=JWT 시크릿키
AWS_ACCESS_KEY_ID=AWS IAM 키 ID
AWS_SECRET_ACCESS_KEY=AWS IAM 시크릿 액세스 키
AWS_BUCKET=S3 버킷 이름

config폴더와 .env는 외부에 노출되면 안되므로 .gitignore에 꼭 추가해주어야합니다.

다음은 sequelize의 index파일입니다. 불필요한 임포트 부분은 생략했습니다.

import { Sequelize } from 'sequelize-typescript';
const env = process.env.NODE_ENV || 'development';
const config = require('../../config/config')[env];
const { username, password, database, dialect, host } = config;

// TODO: Connect to MySQL
export const sequelize = new Sequelize({
  username,
  password,
  database,
  dialect,
  host,
  models: [
    User,
    Post,
    Musics,
    Comment,
    PlayList,
    Follow,
    UserComment,
    MusicPlayList,
    UserMusicsLike,
    UserPostLike
  ]
});

// TODO: Check connection
sequelize
  .authenticate()
  .then(() => console.log('Connection Successed!'))
  .catch((err: Error) => console.error('Unable to connect to the MySQL', err));

Sequelize를 불러와서 config의 정보를 추가하고, 각 모델파일들을 추가해줍니다.

각 파일들은 모두 model폴더 내에서 관리했으며 모델을 하나만 가져와보겠습니다.

@를 이용해 각 테이블, 컬럼의 정보를 표현합니다.

sequelize는 PrimaryKey ID를 따로 설정하지 않으면 자동으로 생성합니다.

각 연결은 @BelongsTo, @HasMany, @BelongsToMany 로 관계를 지을 수 있습니다.

import {
  Table,
  Column,
  Model,
  DataType,
  AllowNull,
  Unique,
  HasMany,
  BelongsToMany
} from 'sequelize-typescript';

// TODO: Users Table
@Table
export class User extends Model<User> {
  @Unique
  @AllowNull(false)
  @Column(DataType.STRING(30))
  email: string;

  @AllowNull(false)
  @Column(DataType.STRING(120))
  password: string;

  @Unique
  @AllowNull(false)
  @Column(DataType.STRING(30))
  username: string;

  @AllowNull(true)
  @Column(DataType.STRING(120))
  userImage: string;

  @AllowNull(true)
  @Column(DataType.TEXT)
  intro: string;

  /* User-Follow #1 */
  @HasMany(() => Follow, 'followingId')
  followers: Follow[];

  /* User-Follow #2 */
  @HasMany(() => Follow, 'followerId')
  following: Follow[];

  /* User-Post */
  @HasMany(() => Post)
  posts: Post[];

  /* User-PostLike */
  @HasMany(() => UserPostLike)
  likePosts: UserPostLike[];

  /* User-PlayList */
  @HasMany(() => PlayList)
  playlists: PlayList[];

  /* User-Comment */
  @BelongsToMany(() => Comment, () => UserComment)
  comments: Comment[];

  /* User-Music */
  @BelongsToMany(() => Musics, () => UserMusicsLike)
  likeMusics: Musics[];
}

그 외 라우터 설정이나 모델 설정 등은 특별할게 없으므로 다루지 않겠습니다.


5. 프론트 입장에서의 백엔드 구성

다음 슬라이드는 제가 4주 프로젝트 발표자료로 썼던 자료입니다.

https://slides.com/geonhwi/deck-1

몇 가지 제가 강조했던 내용은 다음과 같습니다.

  1. 항상 프론트가 줄 수 있는 값을 생각한다.
    REQEST의 params나 body로 무언갈 요구할 때 프론트가 줄 수 있는지 생각해야합니다.
    예시에서는 정말 단순한 예시였지만, 생각 안하고 적을 경우 정말 큰 실수가 발생합니다.

  2. EC2 서버의 배포는 항상 빠르게
    서버의 시작은 HELLO WORLD를 배포하는 것부터 시작한다고도 합니다.
    그정도로 서버에서 배포를 빨리 해주어야 원활한 프론트 작업이 진행됩니다.
    부담 될 정도로 완벽히 할 필요없습니다. 초반 로그인 등의 작업만 해두셔도 좋습니다.

  3. 성공이 아닌 실패의 경우로 생각한다.
    단순하게 로그인에 성공하면 res.json({ message: '로그인 성공!' })
    나머지 경우는 next(err)로 보내면 클라이언트는 어떤 경우에 어떤 실패가 일어났는지
    알 수 없으며, 프론트에서 모든 작업을 다 해주어야 합니다.
    그보단 먼저 실패할 수 있는 경우를 먼저 생각하고 마지막에 성공을 구현한다면
    훨씬 더 좋은 response를 전달할 수 있다고 생각합니다.
    아이디가 중복일 경우, 비밀번호가 비었을 경우, 자릿수가 부족할 경우 등 말이죠.

6. 결론

이렇게 5번 까지나 적으면서 리뷰를 해보았지만 4주 프로젝트중 가장 중요한 것은 한가지였습니다.

프론트와의 소통입니다.

저는 백엔드를 혼자 진행했지만, 두 명, 세명도 상관없습니다.

코드를 수정할 때, PR을 날릴 때, merge를 할 때, 서버를 끄고 켤 때.

항상 프론트와 소통하는 것이 중요합니다.

내가 무슨 작업을 하고 있고, 무엇을 했고, 어디가 힘들고, 어디가 잘 되었는지.

자신이 제대로 설명하지 않으면 아무도 알 수 없습니다.

코드의 깔끔함, 뭐 그런거 다 좋죠. 하지만 협업이란 그런게 아닙니다.

항상 자신의 코드를 누군가에게 설명하고, 이해시키고,

프로젝트가 끝났을 때 제 코드를 팀원 모두가 알아볼 수 있을 때, 완벽한 프로젝트라 생각합니다.

그렇게 완벽한 프로젝트는 아니었지만, 협업에 관해서는 많이 배울 수 있는 좋은 시간이었습니다.

앞으로 잡서칭 기간이 남았지만, 배운 스택들을 복습하면서 차분히 진행해보려합니다.


해당 스크린샷 및 GIF

아래는 제가 맡았던 프론트엔드 부분 스크린샷 및 GIF 동작입니다.







감사합니다 🙇🏻‍♂️

profile
ggit

2개의 댓글

comment-user-thumbnail
2020년 6월 4일

안녕하세요! 이제 막 코드스테이츠 프리과정을 마친 수강생입니다ㅎㅎ 이머시브의 꽃이라고 말할 수 있는 프로젝트에 대한 상세한 과정과 협업에 대한 팁을 알려주셔서 감사합니다:)

1개의 답글