학이시습

[AnM] 앱앤미 웹사이트 Node.js 소스코드 해석 본문

AnM

[AnM] 앱앤미 웹사이트 Node.js 소스코드 해석

dbswndud 2024. 2. 12. 19:51

Node.js 소스코드 ↓

더보기
const express = require('express');
const path = require('path');
const mysql = require('mysql2');
const bodyParser = require('body-parser');

const app = express();
const port = 3000;

// EJS를 템플릿 엔진
app.set('view engine', 'ejs');

// 정적 파일 제공 설정
app.use(express.static(path.join(__dirname, 'public')));

// db
const db = require('./database/database');

// body-parser 설정
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

// 라우터 추가
const indexRouter = require('./routes/index');              // 인덱스
const membersRouter = require('./routes/members');          // 부원 소개
const applicationRouter = require('./routes/application');  // 지원하기
const qnaRouter = require('./routes/qna');                   // QnA
const recordsRouter = require('./routes/records');          // 활동 기록
app.use('/', indexRouter);
app.use('/members', membersRouter);
app.use('/application', applicationRouter);
app.use('/qna', qnaRouter);
app.use('/records', recordsRouter);

// 학생 정보 등록 API
app.post('/students', (req, res) => {
  const student = req.body;

  // student_id가 없다면 기본값으로 설정
  const student_id = student.student_id || 'DefaultStudentID';

  // 학번 데이터를 가져와서 student_id에 넣기
  const studentData = { ...student, student_id };

  db.query('INSERT INTO students SET ?', studentData, (err, result) => {
    if (err) {
      console.error('MySQL query error:', err);
      res.status(500).json({ error: '학생 정보가 들어가지 못했습니다!!' });
    } else {
      res.status(201).json({ message: '학생 정보가 성공적으로 들어갔습니다!!' });
    }
  });
});


// 학생 정보 조회 API
app.get('/students', (req, res) => {
  db.query('SELECT * FROM students', (err, results) => {
    if (err) {
      console.error('MySQL query error:', err);
      res.status(500).json({ error: '학생 정보를 불러오지 못했습니다!!' });
    } else {
      res.status(200).json(results);
    }
  });
});

// 새로운 메시지 등록 API
app.post('/messages', (req, res) => {
  const message = req.body;

  db.query('INSERT INTO qna SET ?', message, (err, result) => {
    if (err) {
      console.error('MySQL query error:', err);
      res.status(500).json({ error: '메시지가 등록되지 못했습니다!!' });
    } else {
      res.status(201).json({ message: '메시지가 성공적으로 등록되었습니다!!' });
    }
  });
});

// 메시지 조회 API
app.get('/messages', (req, res) => {
  db.query('SELECT * FROM qna', (err, results) => {
    if (err) {
      console.error('MySQL query error:', err);
      res.status(500).json({ error: '메시지를 불러오지 못했습니다!!' });
    } else {
      res.status(200).json(results);
    }
  });
});

// 메시지 답변 등록 API
app.post('/messages/:id/replies', (req, res) => {
    const messageId = req.params.id;
    const replyText = req.body.reply_text;
  
    if (!messageId || !replyText) {
      return res.status(400).json({ error: '메시지 ID와 답변 내용을 모두 제공해야 합니다.' });
    }
  
    db.query('INSERT INTO message_replies (message_id, reply_text) VALUES (?, ?)', [messageId, replyText], (err, result) => {
      if (err) {
        console.error('MySQL query error:', err);
        res.status(500).json({ error: '메시지 답변을 등록하지 못했습니다.' });
      } else {
        res.status(201).json({ message: '메시지 답변이 성공적으로 등록되었습니다.' });
      }
    });
  });
  
  // 메시지 및 답변 조회 API
  app.get('/messages-with-replies', (req, res) => {
    db.query('SELECT qna.*, message_replies.reply_text, message_replies.reply_time FROM qna LEFT JOIN message_replies ON qna.id = message_replies.message_id', (err, results) => {
      if (err) {
        console.error('MySQL query error:', err);
        res.status(500).json({ error: '메시지 및 답변을 불러오지 못했습니다.' });
      } else {
        res.status(200).json(results);
      }
    });
  });

 // 학생 정보 조회 페이지 라우터
  app.get('/students-list', (req, res) => {
    db.query('SELECT * FROM students', (err, results) => {
      if (err) {
        console.error('MySQL query error:', err);
        res.status(500).send('Internal Server Error');
      } else {
        res.render('students', { students: results });
      }
    });
  });

  
// 서버 시작
app.listen(port, () => {
  console.log(`http://localhost:${port}`);
});

코드가 길기 때문에 잘라서 해석해 보도록 하겠습니다.


소스코드

const express = require('express');
const path = require('path');
const mysql = require('mysql2');
const bodyParser = require('body-parser');

const app = express();
const port = 3000;

해석

더보기

상수인 'express'에 express 모듈을 가져와 대입

상수인 'path'에 path 모듈을 가져와 대입

상수인 'mysql'에 mysql2 모듈을 가져와 대입

상수인 'bodyParser'에 body-parser 모듈을 가져와 대입

 

상수인 'app'에 express() 대입        // 새로운 express 생성

상수인 'port'에 3000 대입               // 포트 번호 지정

 

모듈을 사용하기 위해 모듈을 불러와 상수에 대입하는 구문들임!

 

require() 함수란?

require는 '요구하다'라는 뜻으로 require() 함수는 외부 모듈을 가져오는 함수입니다.

require('express')는 express라는 모듈을 가져오는(요구하는) 것이죠.

그럼 여기서 모듈은 무엇일까요?

 

모듈이란?

프로그램을 구성하는 구성요소로 관련된 데이터와 함수를 하나로 묶은 단위입니다.

Node.js는 npm(node package manager)을 사용합니다. npm에는 여러 모듈이 있습니다.

일일이 모든 코드를 적지 않고 npm에서 불러와서 완성시킬 수 있습니다..

 

express 모듈

express 모듈은 웹 및 모바일 애플리케이션을 위한 일련의 강력한 기능을 제공하는 간결하고 유연한 Node.js 웹 애플리케이션 프레임워크입니다.

프레임워크를 만들고 사용하는 역할을 해줍니다.

 

path 모듈

path 모듈은 폴더와 파일의 경로를 쉽게 조작하도록 도와주는 모듈입니다.

운영체제별로 경로 구분자가 다르기 때문에 path 모듈이 필요합니다.

 

mysql2 모듈

mysql 모듈은 MySQL에 접근하기 위해 쓰이는 모듈입니다. mysql 모듈도 있지만 mysql2 모듈을 쓰는 이유는 promise 때문입니다. (대충 mysql2가 mysql의 업그레이드 버전 같습니다.)

 

body-parser 모듈

body-parser 모듈은 클라이언트가 보내는 request data의 body로부터 파라미터를 편리하게 추출해 주는 모듈입니다.

body-parser는 미들웨어인데 미들웨어 없이 req.body에 접근하면 기본으로 undefined가 설정되어 있으므로 미들웨어를 사용하여 요청 데이터 값에 접근해야 합니다. (정말 필요하다는 뜻)

 

미들웨어란?

요청을 받고 요청을 보내기 위해 중간(미들)에 목적에 맞게 처리하는 함수들입니다.


소스코드

// EJS를 템플릿 엔진
app.set('view engine', 'ejs');

// 정적 파일 제공 설정
app.use(express.static(path.join(__dirname, 'public')));

// db
const db = require('./database/database');

// body-parser 설정
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

해석

더보기

EJS 설치

 

미들웨어 추가

 

./database/database를 가져와 db에 대입

 

JSON 미들웨어 추가 

바디 데이터를 파싱 하기 위한 구문 

 

EJS 모듈

문법과 설정에 따라 html 형식으로 변환시키는 모듈입니다.

 

set() 메소드

set() 메소드는 웹 서버의 환경을 설정하는데 필요한 메소드입니다. set() 메소드는 다음과 같이 사용합니다.

app.set('title', 'myServer');

 

use() 메소드

use() 메소드는 미들웨어를 사용하기 위한 메소드입니다. use() 메소드는 다음과 같은 사용방법도 있지만 여러 가지의 사용방법이 있습니다.

app.use(미들웨어)

 


소스코드

// 라우터 추가
const indexRouter = require('./routes/index');              // 인덱스
const membersRouter = require('./routes/members');          // 부원 소개
const applicationRouter = require('./routes/application');  // 지원하기
const qnaRouter = require('./routes/qna');                   // QnA
const recordsRouter = require('./routes/records');          // 활동 기록
app.use('/', indexRouter);
app.use('/members', membersRouter);
app.use('/application', applicationRouter);
app.use('/qna', qnaRouter);
app.use('/records', recordsRouter);

해석

더보기

./routes/index를 가져와 indexRouter에  대입

./routes/members를 가져와 membersRouter에  대입

./routes/application을 가져와 applicationRouter에  대입

./routes/qna를 가져와 qnaRouter에  대입

./routes/records를 가져와 recordsRouter에  대입

'/'로 시작하는 모든 요청에서 indexRouter 실행

'/members'로 시작하는 모든 요청에서 membersRouter 실행

'/application '로 시작하는 모든 요청에서 applicationRouter 실행

'/qna'로 시작하는 모든 요청에서 qnaRouter 실행

'/records'로 시작하는 모든 요청에서 recordsRouter 실행

 

라우터

클라이언트의 요청 경로를 보고 이 요청을 처리할 수 있는 곳으로 기능을 전달해 주는 역할을 합니다.

라우터를 이용하면 가독성을 좋게 하고 코드를 간략화한다.

 

use('/user', 미들웨어) 메소드

use('/user', 미들웨어) 메소드는 user로 시작하는 모든 요청에서 미들웨어를 실행합니다.

 


소스코드

// 학생 정보 등록 API
app.post('/students', (req, res) => {
  const student = req.body;

  // student_id가 없다면 기본값으로 설정
  const student_id = student.student_id || 'DefaultStudentID';

  // 학번 데이터를 가져와서 student_id에 넣기
  const studentData = { ...student, student_id };

  db.query('INSERT INTO students SET ?', studentData, (err, result) => {
    if (err) {
      console.error('MySQL query error:', err);
      res.status(500).json({ error: '학생 정보가 들어가지 못했습니다!!' });
    } else {
      res.status(201).json({ message: '학생 정보가 성공적으로 들어갔습니다!!' });
    }
  });
});

해석

더보기

post 메소드를 이용해 /students에 새로운 리소스 생성

student에 req.body 대입

 

student_id에 student.student_id 또는 DefaultStudentID 대입      // ||연산자는 가장 먼저 있는 true를 반환함

 

 

// db.query(실행할 쿼리, 쿼리에 필요한 데이터, 호출될 콜백 함수)

DB에서 'INSERT INTO students SET ?' 실행

만약 에러이면

콘솔에 에러 메시지와 에러를 띄워서 응답하고

클라이언트로 에러를 띄워 에러 메시지 응답

에러가 아니면

성공적으로 정보가 들어갔다는 메시지 응답

 

post 메소드

post 메소드는 주로 새로운 리소스를 생성(Create)하는 데 사용합니다.

성공적으로 완료하면 201(Created) HTTP 응답을 반환합니다.

 

HTTP 상태 코드

위의 코드처럼 HTTP 응답 코드(500, 201 등)를 통해 서버에서의 처리 결과를 파악할 수 있습니다.

  • 2XX : 성공을 뜻합니다. 
    • 201(작성됨) : 성공적으로 요청되었으며 서버가 새 리소스를 작성했음
  • 5XX : 서버 오류를 뜻합니다.
    • 500(내부 서버 오류) : 서버 오류가 발생하여 요청을 수행할 수 없음

현재 100~500번 대까지 상태 코드가 정의되어 있는데 첫 번째 숫자에 따라 5가지로 분류해서 사용하고 있습니다.

HTTP 상태 코드 보기 (위키백과)

 


소스코드

// 학생 정보 조회 API
app.get('/students', (req, res) => {
  db.query('SELECT * FROM students', (err, results) => {
    if (err) {
      console.error('MySQL query error:', err);
      res.status(500).json({ error: '학생 정보를 불러오지 못했습니다!!' });
    } else {
      res.status(200).json(results);
    }
  });
});

해석

더보기

get 메소드를 이용해 /student로 들어온 것을 조회함 

DB에서 'SELECT * FROM students' 실행

만약 에러이면 (에러 처리)

콘솔에 에러 메시지와 에러를 띄워서 응답하고

클라이언트로 오류 메시지 띄워 응답

에러가 아니면 (else)

결과 응답

 

get 메소드

get 메소드는 주로 데이터를 읽거나(Read) 검색(Retrieve)할 때 사용하는 메소드입니다.

get 메소드는 URL에 데이터를 포함하여 데이터 조회에 적합합니다.

 

HTTP 상태 코드 - 200

200(성공) : 서버가 요청을 제대로 처리했다는 뜻입니다

 


소스코드

// 새로운 메시지 등록 API
app.post('/messages', (req, res) => {
  const message = req.body;

  db.query('INSERT INTO qna SET ?', message, (err, result) => {
    if (err) {
      console.error('MySQL query error:', err);
      res.status(500).json({ error: '메시지가 등록되지 못했습니다!!' });
    } else {
      res.status(201).json({ message: '메시지가 성공적으로 등록되었습니다!!' });
    }
  });
});

해석

더보기

post 메소드를 이용해 /message에 새로운 리소스 생성

message에 req.body 대입

 

DB에서 'INSERT INTO qna SET ?' 실행

만약 에러이면

콘솔에 에러 메시지와 에러를 띄어서 응답하고 

클라이언트에 에러 메세지 띄워 응답

에러가 아니면

성공적으로 메시지가 등록되었다는 메시지 응답

 


소스코드

// 메시지 조회 API
app.get('/messages', (req, res) => {
  db.query('SELECT * FROM qna', (err, results) => {
    if (err) {
      console.error('MySQL query error:', err);
      res.status(500).json({ error: '메시지를 불러오지 못했습니다!!' });
    } else {
      res.status(200).json(results);
    }
  });
});

해석

더보기

get 메소드를 이용해 /messages로 들어온 것을 조회

DB에서 'SELECT * FROM qna' 실행

만약 에러이면 (에러 처리)

콘솔에 에러 메시지와 에러를 띄워서 응답하고

클라이언트로 오류 메시지 띄워 응답

에러가 아니면 (else)

결과 응답

 


소스코드

// 메시지 답변 등록 API
app.post('/messages/:id/replies', (req, res) => {
    const messageId = req.params.id;
    const replyText = req.body.reply_text;
  
    if (!messageId || !replyText) {
      return res.status(400).json({ error: '메시지 ID와 답변 내용을 모두 제공해야 합니다.' });
    }
  
    db.query('INSERT INTO message_replies (message_id, reply_text) VALUES (?, ?)', [messageId, replyText], (err, result) => {
      if (err) {
        console.error('MySQL query error:', err);
        res.status(500).json({ error: '메시지 답변을 등록하지 못했습니다.' });
      } else {
        res.status(201).json({ message: '메시지 답변이 성공적으로 등록되었습니다.' });
      }
    });
  });

해석

더보기

post 메소드를 이용해 /messages/:id/replies에 새로운 리소스 생성

messageId에 req.params.id 대입

replyText에 req.body.reply_text 대입

 

만약 !messageId 또는 !replyText (messageId가 없거나 replyText가 없다면)라면

클라이언트로 에러 메시지를 응답(리턴)

 

DB에서 'INSERT INTO ... (?, ?)' 실행

만약 에러이면

콘솔에 에러 메시지와 에러를 띄워서 응답하고

클라이언트로 오류를 띄워 에러 메시지 응답

에러가 아니면

성공적으로 답변이 등록되었다는 메시지 응답

 

HTTP 상태 코드 - 400

400(잘못된 요청) : 서버가 요청의 구문을 인식하지 못했다는 뜻입니다.

 


소스코드

  // 메시지 및 답변 조회 API
  app.get('/messages-with-replies', (req, res) => {
    db.query('SELECT qna.*, message_replies.reply_text, message_replies.reply_time FROM qna LEFT JOIN message_replies ON qna.id = message_replies.message_id', (err, results) => {
      if (err) {
        console.error('MySQL query error:', err);
        res.status(500).json({ error: '메시지 및 답변을 불러오지 못했습니다.' });
      } else {
        res.status(200).json(results);
      }
    });
  });

해석

더보기

get 메소드를 이용해 /messages-with-replies로 들어온 것을 조회

DB에서 'SELECT qna. *, ..., ' 실행

만약 에러이면 (에러 처리)

콘솔에 에러 메시지와 에러를 띄워서 응답하고

클라이언트로 오류 메시지 띄워 응답

에러가 아니면 (else)

결과 응답

 


소스코드

 // 학생 정보 조회 페이지 라우터
  app.get('/students-list', (req, res) => {
    db.query('SELECT * FROM students', (err, results) => {
      if (err) {
        console.error('MySQL query error:', err);
        res.status(500).send('Internal Server Error');
      } else {
        res.render('students', { students: results });
      }
    });
  });

해석

더보기

get 메소드를 이용해 /students-list로 들어온 것을 조회

DB에서 'SELECT * FROM students' 실행

만약 에러이면 (에러 처리)

콘솔에 에러 메시지와 에러를 띄워서 응답하고

클라이언트로 오류 메시지 띄워 응답

에러가 아니면 (else)

결과 응답

 


소스코드

// 서버 시작
app.listen(port, () => {
  console.log(`http://localhost:${port}`);
});

해석

더보기

원하는 서버에 포트 오픈

콘솔에 로컬 호스트를 찍습니다.

 

app.listen

서버를 시작하기 위해 사용합니다.

port에서 계속 듣고 있다는..