[Node.js] Pagination & Session

Comely·2025년 6월 9일

Node.js

목록 보기
11/14

Pagination (페이지네이션)

기본 개념

  • 많은 데이터를 여러 페이지로 나누어 보여주는 기능
  • DB 부담과 브라우저 성능 최적화를 위해 필요

URL 구조 설계

/list/1  → 1~5번째 글
/list/2  → 6~10번째 글  
/list/3  → 11~15번째 글

기본 구현 (하드코딩)

app.get('/list/1', async (요청, 응답) => {
  let result = await db.collection('post').find().skip(0).limit(5).toArray()
  응답.render('list.ejs', { 글목록 : result })
})

URL 파라미터 활용

app.get('/list/:id', async (요청, 응답) => {
  let result = await db.collection('post').find()
    .skip((요청.params.id - 1) * 5)
    .limit(5)
    .toArray()
  응답.render('list.ejs', { 글목록 : result })
})

MongoDB 쿼리 메소드

  • .limit(5) : 최대 5개 문서만 가져오기
  • .skip(5) : 앞의 5개 문서 건너뛰기
  • .toArray() : 결과를 배열로 변환

skip() 방식의 한계

  • 큰 숫자(100만 이상) skip 시 성능 저하
  • 문서를 하나씩 세어가며 처리하기 때문

성능 최적화된 Pagination

_id 기반 방식

await db.collection('post').find({
  _id: { $gt: 마지막게시물_id }
}).limit(5).toArray()

장점과 단점

  • 장점: 매우 빠른 성능 (_id 기반 검색은 DB가 가장 빠르게 처리)
  • 단점: 순차적 탐색만 가능, 특정 페이지로 점프 불가

다음 버튼 구현

// 서버 코드
app.get('/list/next/:id', async (요청, 응답) => {
  let result = await db.collection('post').find({
    _id: { $gt: new ObjectId(요청.params.id) }
  }).limit(5).toArray()
  응답.render('list.ejs', { 글목록 : result })
})
<!-- EJS 템플릿 -->
<a href="/list/next/<%= 글목록[글목록.length - 1]._id %>">다음</a>

Auto Increment 구현

  • MongoDB는 기본 auto increment 미지원
  • Counter 컬렉션 활용하여 구현
  • Trigger 기능 사용 권장

Session 기반 회원 인증

기본 동작 원리

회원가입
1. 유저가 아이디/비번 입력
2. 서버가 DB에 저장

로그인
1. 유저가 로그인 시도
2. DB의 정보와 대조 확인
3. 성공 시 세션 생성쿠키 발급

인증이 필요한 기능
1. 유저가 요청 시 쿠키 자동 전송
2. 서버가 세션 확인 후 서비스 제공

입장권(세션) 저장 방식

  • 브라우저 쿠키에 자동 저장
  • GET/POST 요청 시 자동으로 함께 전송

Session vs JWT 비교

Session 방식

  • DB에 세션 정보 저장
  • 매 요청마다 DB 조회 필요
  • 장점: 엄격한 사용자 검증 가능
  • 단점: DB 부담 증가

JWT(Token) 방식

  • 사용자 정보를 암호화하여 토큰에 저장
  • DB 조회 없이 토큰 검증
  • 장점: DB 부담 적음, 확장성 좋음
  • 단점: 토큰 탈취 시 강제 로그아웃 어려움

Passport 라이브러리 설정

설치

npm install express-session passport passport-local

기본 설정

const session = require('express-session')
const passport = require('passport')
const LocalStrategy = require('passport-local')

app.use(passport.initialize())
app.use(session({
  secret: '암호화에 쓸 비번',
  resave: false,
  saveUninitialized: false
}))
app.use(passport.session())

로그인 검증 로직

passport.use(new LocalStrategy(async (입력한아이디, 입력한비번, cb) => {
  let result = await db.collection('user').findOne({ username: 입력한아이디 })
  if (!result) {
    return cb(null, false, { message: '아이디 DB에 없음' })
  }
  if (result.password == 입력한비번) {
    return cb(null, result)
  } else {
    return cb(null, false, { message: '비번불일치' })
  }
}))

로그인 API

app.post('/login', async (요청, 응답, next) => {
  passport.authenticate('local', (error, user, info) => {
    if (error) return 응답.status(500).json(error)
    if (!user) return 응답.status(401).json(info.message)
    요청.logIn(user, (err) => {
      if (err) return next(err)
      응답.redirect('/')
    })
  })(요청, 응답, next)
})

세션 생성

passport.serializeUser((user, done) => {
  process.nextTick(() => {
    done(null, { id: user._id, username: user.username })
  })
})

세션 검증

passport.deserializeUser(async (user, done) => {
  let result = await db.collection('user').findOne({
    _id: new ObjectId(user.id)
  })
  delete result.password
  process.nextTick(() => {
    return done(null, result)
  })
})

세션 유효기간 설정

app.use(session({
  secret: '암호화에 쓸 비번',
  resave: false,
  saveUninitialized: false,
  cookie: { maxAge: 60 * 60 * 1000 } // 1시간
}))

로그인 상태 확인

  • API 내에서 요청.user로 현재 로그인된 사용자 정보 확인 가능
  • 브라우저 개발자도구 → Application → Cookies에서 세션 쿠키 확인
profile
App, Web Developer

0개의 댓글