eunja511005 / AutoCoding

0 stars 0 forks source link

[설치] Node.js 서비스를 위한 환경 구축 #175

Open eunja511005 opened 3 months ago

eunja511005 commented 3 months ago

1. OS 버젼 확인

cat /etc/*release

image

2. nvm 및 node.js 18.14.1 버젼 설치(nvm은 yum으로 설치 불가)

1. cd /home/opc/download
2. wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
3. 환경 변수 때문에 신규 세션 열어서 정상 확인 가능
4. nvm --version
5.  nvm install 18.14.1 : node.js 18.14.1 버젼 설치
6. node -v 명령어 통해 버젼 확인

3. 디렉토리 생성 및 권한

mkdir -p /home/opc/node/service/helloProject
chmod -R 755 /home/opc/node/service/helloProject

4. node.js 테스트 프로젝트 만들어 보기

1. cd /home/opc/node/service/helloProject
3. npm init -y : npm 프로젝트 초기화(y 옵션은 모든 질문에 y로 대답), 초기화 하면 package.json 자동 생성 
6. npm install express : 웹서비스 제공을 위한 경량 프레임워크인 Express를 프로젝트에 설치
7. npm install axios : 다른 API 호출시 필요한 axios 설치
8. npm install -g localtunnel : 프로젝트에 localtunnel을 설치하여 외부에서 접속할 수 있도록(g 옵션은 전역으로 설치하여 어느 위치에서든 사용할 수 있게)
9. Notepad++로 package.json 열어서 수정(npm run dev 통해 서비스 시작 가능하도록 dev 스크립트 추가)
10. mkdir src : 소스 디렉토리 생성
11. cd src
12. vi app.js: 애플리케이션 서비스 메인(수정은 Notepad++로)
13. vi getUserProgress.js : 유저 조회를 위한 소스(수정은 Notepad++로)
14. vi postNewUser.js : 유저 생성을 위한 소스(수정은 Notepad++로)
15. chmod -R 755 /home/opc/node/service/helloProject
16. node /home/opc/node/service/helloProject/src/app.js 서비스 시작 
17. lt --port 3000 : 신규 세션 열어서 외부에서 3000 접속 가능 하게 url 생성 
18. 생성된 url로 접속 하면 패스워드를 넣으라고 한다.
19. 다시 모바X텀에서 세션 하나 더 열어서 curl https://loca.lt/mytunnelpassword 실행 하면 IP 처럼 생긴게 나옴
20. 이걸 브라우져의 아래 화면 캡쳐처럼 복사한 후 서브밋 하면 됨

image

image

image

package.json 소스

{
  "name": "20240321_new_actions",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "node src/app.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "axios": "^1.6.8",
    "express": "^5.0.0-beta.2"
  }
}

app.js 소스

const PROMPT_MANAGER = Object.freeze({
    AD1: 'Let users know that if they don\'t see a course they want, they can contact jaewng.yun@samsung.com to request.',
    AD2: 'If courseName is \'Not Registered\' provide a link to "samsungsds.com" with marketing copy so users can enroll right away.\n '+
        'Because it\'s winter break, we\'re currently discounting $30 to $15. the original price of $30 must be with a strikethrough.',
    AD3: 'Celebrate the new user signup with all sorts of bells and whistles.',
    RG1: 'The request courseId must be a number.',
    RG2: 'Invalid input: name and lecture are required.',
    RF1: 'When providing a list, be sure to provide it as a markdown table with the headers profile, courseName, and progress.\n ' +
        'Do not reverse the order of the headers. Profile consists of photo + name\n ' +
        'In particular, the person who made the most progress is marked as the winner (with an emoji),',
    RF2: 'Name and lecture must be MD title format. e.g) ## lecture: someLecture \n ## name: John Doe'
})

const express = require('express');
const getUsersProgress = require('./getUserProgress');
const postNewUser = require('./postNewUser')
const app = express();
app.use(express.json());

app.get('/', (req, res) => {
    res.send('Hello World!');
});

// 응답 객체를 감싸는 미들웨어
function wrapResponses(req, res, next) {
    const originalSend = res.send;

    // 새로운 send 함수를 정의
    res.send = function (body) {
        if (typeof body === 'string') {
            try {
                body = JSON.parse(body);
            } catch (e) {
                // body가 JSON이 아닐 경우
            }
        }

        // 특정 키워드가 포함되었는지 확인
        if (body.ADDITIONAL_INFORMATION_FOR_ASSISTANT || body.REQUEST_GUIDELINES_FOR_ASSISTANT || body.RESPONSE_FORMATTING_GUIDELINES_FOR_ASSISTANT) {
            console.log("특별한 키워드가 포함된 응답 발견!");
            // 여기에 키워드에 따른 추가 처리를 구현할 수 있음
            body.ADDITIONAL_INFORMATION_FOR_ASSISTANT = PROMPT_MANAGER[body.ADDITIONAL_INFORMATION_FOR_ASSISTANT] ?? 'If Error Occur, Please contact the administrator at \'jaewng.yun@samsung.com\'.';
            body.REQUEST_GUIDELINES_FOR_ASSISTANT = PROMPT_MANAGER[body.REQUEST_GUIDELINES_FOR_ASSISTANT] ?? 'default';
            body.RESPONSE_FORMATTING_GUIDELINES_FOR_ASSISTANT = PROMPT_MANAGER[body.RESPONSE_FORMATTING_GUIDELINES_FOR_ASSISTANT] ?? 'default';
        }

        // 원래의 send 함수를 호출하여 실제 응답 전송
        return originalSend.call(this, JSON.stringify(body));
    };

    next();
}

app.use(wrapResponses);

app.get('/progress', getUsersProgress);
app.post('/users', postNewUser);

const port = 3000;
app.listen(port, () => {
    console.log('Sever is listening at http://localhost:' + port);
})

getUsersProgress.js 소스

const axios = require('axios');

const courses = {
    101: 'LLM application',
    102: 'ChatGPT 활용하기'
}

async function getUsersProgress(req, res) {  
    try {  
        // Express에서 쿼리스트링 파라미터를 가져옴
        const courseIdQuery = req.query.courseId;
        const courseName = courseIdQuery ? courses[courseIdQuery] : null;

        // 존재하지 않는 강의 ID인 경우 사용 가능한 강의 ID 목록을 반환  
        if (courseIdQuery && !courseName) {   
            res.status(400).json({
                error: "Invalid Course ID",
                availableCourseIds: Object.entries(courses).map(([id, courseName]) => ({ id, courseName })),
                ADDITIONAL_INFORMATION_FOR_ASSISTANT: 'AD1',
                REQUEST_GUIDELINES_FOR_ASSISTANT: 'RG1',
            });
            return;
        }

        // reqres.in에서 사용자 목록을 가져옴  
        const response = await axios.get('https://reqres.in/api/users');  

        // 강의 진행 상황을 시뮬레이션하기 위한 임시 데이터
        const courseProgress = {  
            '1': { courseName: 'LLM application', progress: 'Chapter 1' },  
            '2': { courseName: 'ChatGPT 활용하기', progress: 'Chapter 2' },  
            '3': { courseName: 'LLM application', progress: 'Chapter 4' },  
            // ... 나머지 사용자에 대한 진행 상황  
        };  

        // 사용자 정보와 함께 강의 진행 상황을 매핑
        const usersWithProgress = response.data.data  
            .map(user => {  
                return {  
                    id: user.id,  
                    name: user.first_name + ' ' + user.last_name,  
                    avatar: user.avatar,
                    progress: courseProgress[user.id]?.progress ?? 'Not started',  
                    courseName: courseProgress[user.id]?.courseName ?? 'Not Registered'  
                };  
            })  
            .filter(user => !courseName || user.courseName === courseName);  

        // 결과를 JSON 형태로 반환
        res.status(200).json({
            usersWithProgress,
            ADDITIONAL_INFORMATION_FOR_ASSISTANT: 'AD2',
            RESPONSE_FORMATTING_GUIDELINES_FOR_ASSISTANT: 'RF1',
        });
    } catch (error) {  
        console.error('Error fetching user list:', error);  
        res.status(500).send('Internal Server Error');  
    }  
}  

module.exports = getUsersProgress;

postNewUser.js 소스

const axios = require("axios");

async function postNewUser(req, res) {
    // 유저로 부터 받은 데이터
    const { name, lecture } = req.body;

    // name과 lecture가 모두 요청 본문에 포함되어 있는지 검증
    if (!name || !lecture) {
        return res.status(400).json({
            REQUEST_GUIDELINES_FOR_ASSISTANT: 'RG3'
        });
    }

    try {
        // 외부 API로 POST 요청 보냄
        const response = await axios.post('https://reqres.in/api/users', {
            name,
            lecture
        });

        // 외부 API로부터 받은 응답을 클라이언트로 보냄
        res.status(201).json({
            ADDITIONAL_INFORMATION_FOR_ASSISTANT: 'AD3',
            RESPONSE_FORMATTING_GUIDELINES_FOR_ASSISTANT: 'RF2',
            data: response.data
        });
    } catch (error) {
        // 에러 처리
        res.status(500).json({
            message: 'Error while creating user',
            error: error.message
        });
    }
}

module.exports = postNewUser;