상세 컨텐츠

본문 제목

[Node.js 1] 9장 익스프레스로 SNS 서비스 만들기

23-24/Node.js 1

by Hetbahn 2023. 12. 22. 10:00

본문

728x90

 

 

 

 

 

 

  • 프로젝트 구조 갖추기

- 로그인, 이미지 업로드, 게시글 작성, 해시태그 검색, 팔로잉 등의 기능이 있는 SNS 서비스를 만들어 본다.

- npm init 명령어를 콘솔에서 호출하거나, 직접 package.json을 작성한다.

- scripts 부분에 start 속성으로 "nodemon app"을 작성한다.

$ npm i sequelize mysql2 sequelize-cli
$ npx sequelize init

- 콘솔에 다음을 입력하여  MySQL을 사용한다.

- views 폴더, routes 폴더, public 폴더 passport 폴더를 만든다.

- app.js와 .env 파일을 만든다.

$ npm i express cookie-parser express-session morgan multer dotenv nunjucks
$ npm i -D nodemon

- 필요한 npm 패키지들을 다음과 같이 설치한다.

-  소스코드의 내용은 https://github.com/zerocho/nodejs-book를 참고한다.

- app.js에서 라우터를 찾지 못하면 404 상태코드를 반환하고, 앱을 8001번 포트에 연결한다.

- GET /profile, GET /join, GET / 총 세 페이지로 이루어져 있다.

 

 

 

  • 데이터베이스 세팅하기

- 사용자 테이블, 게시글 테이블, 해시태그 테이블이 필요하므로 models 폴더 안에 user.js와 post.js, hashtag.js를 생성한다.

- user.js에서는 이메일, 닉네임, 비밀번호를 저장하고, SNS 로그인을 했을 경우에는 provider snsId를 저장한다.

- post.js에서는 게시글 내용과 이미지 경로를 저장한다.

- hashtag.js에서는 태그 이름을 저장한다.

- models/index.js에서는  associate 함수 안에 각  모델 간의 관계를 정의한다.

- User 모델과 Post 모델은 1:N 관계이므로 hasMany로 연결한다.

- 팔로잉 기능은 N:M 관계이다. through 옵션을 사용해 모델 이름을 Follow로 지정한다.

- 팔로잉 기능 N:M 관계의 foreignKey 옵션에 각각 followerId, followingId를 넣어 컬럼을 구분한다.

- 같은 테이블 간의 N:M 관계에서는 as 옵션을 넣어야 한다. foreignKey와 반대되는 모델을 가리킨다.

- User 모델과 Post 모델은 1:N 관계이므로 belongsTo로 연결한다.

- Post 모델과 Hashtag 모델은 N:M 관계이므로 belongsToMany로 연결한다.

- 데이터베이스를 생성하기 위해 config.json MySQL 비밀번호를 password 옵션에 넣는다.

$ npx sequelize db:create

- 콘솔에 위와 같이 입력하면 데이터베이스가 생성된다.

...
const pageRouter = require('./routes/page');
const { sequelize } = require('./models');
...
nunjucks.configure('views', {
  express: app,
  watch: true,
});
sequelize.sync({ force: false })
  .then(() => {
    console.log('데이터베이스 연결 성공');
  })
  .catch((err) => {
    console.error(err);
  });

app.use(morgan('dev'));
...

- app.js에서 모델을 서버와 연결 후, npm start를 콘솔에 입력하면 서버가 실행된다.

 

 

 

  • Passport 모듈로 로그인 구현하기

- 소스코드의 내용은 https://github.com/zerocho/nodejs-book를 참고한다.

$ npm i passport passport-local passport-kakao bcrypt

- Passport 관련 패키지들을 설치하고 모듈을 app.js와 연결한다.

- passport/index.js에서 serializeUser는 로그인 시 실행되며, req.session() 객체에 어떤 데이터를 저장할지 정하는 메서드이다. 예제에서는 user.id를 넘긴다.

- deserializeUser는 매 요청 시 실행되며, serializeUser에서 저장했던 id를 받아 데이터베이스에서 사용자 정보를 조회한다. 조회한 정보는 req.user에 저장한다.

 

1. 로컬 로그인 구현하기

- 로그인한 사용자는 회원가입과 로그인 라우터에 접근하면 안 되며, 로그인하지 않은 사용자는 로그아웃 라우터에 접근하면 안 되므로 라우터에 접근 권한을 제어하는 미들웨어가 필요하다.

- routes/middlewares.js에서 로그인 중이면 req.isAuthenticated() true고, 그렇지 않으면 false이다.

- routes/page.js에서 프로필은 req.isAuthenticated() true여야 next가 호출되는 isLoggedIn 미들웨어를 사용한다.

- 회원가입 페이지는 req.isAuthenticated() false일 때만 next를 호출되는 isNotLoggedIn 미들웨어를 사용한다.

- 회원가입 라우터에서는 같은 이메일로 가입한 사용자가 없다면 비밀번호를 암호화하고, 사용자 정보를 생성한다.

- 로그인 라우터에서는 로그인 요청이 들어오면 passport.authenticate('local') 미들웨어가 로컬 로그인 수행한다.

- 로그아웃 라우터에서는 req.user 객체와 req.session 객체의 내용 제거하고, 메인 페이지로 돌아간다.

- passport/localStrategy.js에서는 사용자 데이터베이스에서 일치하는 이메일이 있는지 확인 후, compare 함수로 비밀번호를 비교한다.

- 로그인에 성공하면 done 함수의 두 번째 인수로 사용자 정보를 넣어 보내며, 메인 페이지로 리다이렉트 된다.

 

2. 카카오 로그인 구현하기

- passport/kakaoStrategy.js에서 기존에 카카오를 통해 회원가입한 사용자가 있는지 조회한다.

- 카카오를 통해 회원가입한 사용자가 없다면 회원가입을 진행한다. 사용자를 생성한 뒤 done 함수를 호출한다.

- GET /auth/kakao에서 카카오 로그인 전략을 수행한다.

- 로그인 후 성공 여부 결과를 GET /auth/kakao/callback으로 받는다.

- kakaoStrategy.js에서 사용하는 clientID는 https://developers.kakao.com에서 앱을 만들어 발급받는다.

 

 

  • multer 패키지로 이미지 업로드 구현하기

- 소스코드의 내용은 https://github.com/zerocho/nodejs-book 참고한다.

$ npm i multer

- multer  패키지를 설치한다.

- routes/post.js에서 POST /post/img 라우터와 POST /post 라우터를 만든다.

- POST /post/img 라우터는 이미지 하나를 업로드받은 뒤 이미지의 저장 경로를 클라이언트로 응답한다.

- POST /post 라우터는 게시글 업로드를 처리하는 라우터이다.

- routes/page.js에서는  데이터베이스에서 게시글을 조회한 뒤 결과를 twits에 넣어 렌더링한다.

 

 

 

  • 프로젝트 마무리하기

- routes/user.js에서 POST /user/:id/follow 라우터를 통해 팔로우할 사용자를 데이터베이스에서 조회한 후, addFollowing 메서드로 현재 로그인한 사용자와의 관계를 지정한다.

- routes/page.js를 수정하여 팔로잉/팔로워 숫자와 팔로우 버튼을 표시한다. req.user를 이용해 정보를 가져온다.

- GET /hashtag 라우터를 통해  해시태그로 게시글을 조회한다.

- 해시태그 값이 없는 경우 메인페이지로 돌려보낸다. 해시태그가 있다면 getPosts메서드로 게시글을 가져온다.

 

 

 

 


 

빈칸 채우기 문제 - (빈칸을 드래그해서 답을 확인해 보세요)

 

1. user.js에서 User 모델과 Post 모델은 (  1:N  ) 관계이므로 (  hasMany  )로 연결한다.

2. post.js에서 User 모델과 Post 모델은 (  1:N  ) 관계이므로 (  belongsTo  )로 연결한다.

3. Post 모델과 Hashtag 모델은 (  N:M  ) 관계이므로 (  belongsToMany  )로 연결한다.

4. 같은 테이블 간의 N:M 관계에서는 (  as  ) 옵션을 넣어야 한다.

5. deserializeUser에서 조회한 사용자 정보는 (   req.user  )에 저장된다.

6. 로그인 중이면 req.isAuthenticated() (  true  )고, 그렇지 않으면 (  false  )이다.

7. localStrategy.js에서는 사용자 데이터베이스에서 일치하는 이메일이 있는지 확인 후, (  compare  ) 함수로 비밀번호를 비교한다.

 

 

코드 문제 

 

 1. 다음 if문의 조건문으로 들어갈 코드를 작성하시오. (로그인 중이면 next()를 호출하는 조건문이다.)

// routes/middlewares.js

exports.isLoggedIn = (req, res, next) => {
  if (/*여기에 코드를 작성*/) {
    next();
  } else {
    res.status(403).send('로그인 필요');
  }
};

 답 :

더보기
req.isAuthenticated()

(더보기로 확인)

 

 

 

2. auth 라우터를 app.js에 연결하는 코드를 작성하시오.

// app.js

// ...
const pageRouter = require('./routes/page');
const authRouter = require('./routes/auth');
const { sequelize } = require('./models');
// ...
/*여기에 코드를 작성*/
// ...

답:

더보기

app.use('/auth', authRouter);

(더보기로 확인)

 


출처: 조현영 ,  『Node.js 교과서』 개정판 3판, 길벗, 9.1장 ~ 9.5장

Node.js #1

Editor : 호박

728x90

관련글 더보기