본문 바로가기
시작/TIL(Today I Learned)

230323~230414 React 프로젝트 - 터미널 연습 사이트 만들기

by 백씨네 2023. 4. 17.

React 프로젝트 - 터미널 연습 사이트 만들기

https://www.terminus.run
 

Terminus : 터미널 연습하기

 

www.terminus.run

 

 

지난 프로젝트 이후 1달간 리액트를 배운 후 프로젝트를 진행했습니다.

 

Linux 명령어 연습사이트

MySQL 명령어 연습사이트

 

이번 프로젝트는 리액트를 이용한 프로젝트를 진행하면서, SPA 웹페이지를 만들고, https 설정 및 도메인까지 연결하여 실제 활용 가능한 사이트를 만들었지만, 이 사이트는 개발자를 꿈꾸는 개발자 지망생들의 React Toy Project 이므로 부족한 부분이 많아도 양해 부탁드립니다.

 

목차

1. 팀원 소개

2. 목표

3. DB 스키마

4. 사이트 소개

5. 'Terminus'의 구조와 흐름

6. 'Terminus' 배포 구조와 흐름

7. 개발과정에서 생긴 문제 및 해결 방안

 

 

1. 팀원 소개 

1-1. 이민수 (팀장) : Back-end

 

1-2. 박경철 : Back-end

 

1-3. 백종환 : Front-end

 

 

2. 목표

2-1. 브라우저에서 터미널 환경을 보여주어 Linux, MySQL 명령어를 연습할 수 있는 서비스를 제공한다.

 

2-2. docker을 이용하여 각각의 사용자에게 독립적인 환경을 제공한다.

( 작성해서 시도는 해봤지만, 완성형이 아니기에 조금 더 공부를 진행해서 배포할 예정)

 

 

3. DB 스키마

DB 스키마

처음 구성할 때 웹에서 이용 가능한 리눅스(Linux), MySQL 명령어 연습 사이트를 만드는 것이 목표였기 때문에,

DB스키마가 User, Quiz 정도로 매우 간단했었지만, 그래도 리액트를 이용한 기본적인 CRUD와 USER 별 Point를 이용한 랭킹을 구현해 보자는 의견이 나와서 스키마를 추가했다.

 

 

4.  사이트 소개

 

4-1. 메인 페이지

심플한 메인페이지는

검은색이고 간단하다 보니 밑에 페이지가 있는지 확인하지 못할 것 같아서 스크롤 컴포넌트를 이용하여 아래 페이지를 볼 수 있도록 유도했다.

 

 

4-2. Sign Up 페이지 및 Login In 페이지

회원가입 및 로그인을 위한 페이지 구현

터미널 환경의 컨셉을 위해 타이핑 효과를 주고  ID 중복체크를 위하여 KeyDown 이벤트로 비동기 통신을 했다.

로그인 페이지에서는 로그인 완료 후에 Redux Thunk를 이용하여 로그인한 유저의 ID와 로그인 상태를 로컬 스토리지에 담아서 새로고침의 상황에도 로그인이 풀리지 않도록 했다.

로그인에 성공하게 되면 상태에 의해 Sign Up, Sign In 버튼도 Profile과 Sign out 버튼으로 바뀐다.

 

 

4-3. Quiz 페이지 및 Free Terminal 페이지

 

문제를 풀 수 있는 문제 페이지와, 문제들을 보지 않고 연습을 할 수 있는 프리터미널 페이지를 만들었습니다.

사이트에 핵심이 되는 명령어를 연습할 수 있는 페이지이다.

Redux, Redux-thunk를 이용하여 Linux, MySQL모드의 상태를 관리하고, 터미널 컴포넌트를 렌더링 할 때, 상태에 따른 `Shell Script`를 실행하여 초기 세팅을 만들었다.

 

 

4-4. Exam 모드

Exam 모드로 들어오면 보여주는 페이지

Exam 모드에 진입하면, 4-3단계에서 보여줬던 상단의 지문을 보여주는 상단 컴포넌트가 ExamMode 상태에 의해 변경된다.

Exam 모드를 진입하면 Submit 버튼도 활성화된다.

 

 

4-5. 정답처리

정답처리

Exam모드에서 답을 작성한 후 Enter 키를 통해 명령어를 실행시킨 후 Submit 버튼을 통해 정답을 제출하면, 정답일 경우 10 point를 주고, 이미 제출했던 정답에 대해서는 중복체크를 통해 이미 처리된 문제 문구가 나타난다.

 

정답을 맞춘 문제에 대해선 Profile 페이지에서 확인할 수 있다.

정답을 맞힌 문제에 대해선 Profile 페이지에서 확인할 수 있다. 

 

4-6. Main 페이지 CRUD 및 Ranking 

Main 페이지에서 스크롤 하면 아래 페이지에 CRUD 와 Ranking 이 있다.

로그인한 유저는 방명록을 작성할 수 있고, 로그인하지 않은 유저는 방명록을 보는 기능만 가능하다.

당연히 수정, 삭제 기능은 로그인 한 유저 본인의 글에만 가능하다.

 

 

4-7. 기여자 페이지

단조로웠던 웹페이지가 아쉬워서 이벤트? 같은 느낌으로 만들어본 기여자 페이지이고,

팀원들의 정보 및 사용 기술 스택을 간단하게 넣었다.

 

재밌는 효과를 위해서 플래시  및 스위치 느낌으로 만들어 봤다...^^..

 

 

 

5. 'Terminus'의 사이트 구조와 흐름

 

 

터미널을 필요로 하는 페이지에 진입 시 WebSocket Connection이 되고, 이때 터미널 객체를 생성하고 UserId와 Mode(Linux 또는 MySQL)를 전달하여, User마다 터미널 환경을 생성하여 보여준다.

 

 

6. 'Terminus' 배포 구조와 흐름

 

코드를 작성 후 Push & Merge를 진행하면 GitHub Actions를 통해 변경된 파일을 감지하고 배포 스크립트에 따라 변경된 파일의 빌드를 다시 진행하고, 배포를 진행하는 과정을 거친다. Front와 Back Directory를 감지하도록 하였다.

 

 

 

7. 개발과정에서 생긴 문제 및 해결 방안

 

Front-end

Xterm

🤔 문제 1 : 웹에 보이는 터미널에서 조작을 위한 키가 작동하지 않음.

🙋‍♂️ 원인 : Xterm.js는 응답받은 내용을 터미널 UI로 꾸며주는 역할을 하는 라이브러리이기 때문이다.

❗️ 해결 : 각각의 키매핑 과정을 통해서 유니코드, 이스케이프 시퀀스 등 인식할 수 있도록 하였다. (enter키, ctrl+c 키 등등)

  • 이스케이프 시퀀스 : 이스케이프 시퀀스(escape sequence)란 텍스트나 데이터 스트림에서 특수한 목적이나 동작을 위해 사용되는 문자나 문자열
  • 아스키코드: ****128개의 문자조합을 제공하는 7비트 부호
  • 유니코드 : 아스키코드만으로는 각 나라별 언어를 표현할 수 없기 때문에 이를 해결한 코드

 

🤔 문제 2 : VI 모드 및 VI모드 키매핑

🙋‍♂️ 원인 : 위의 문제와 비슷해 보이지만 vi모드에 진입 시 insert모드 변환 및 방향키 맵핑이 필요하였다.

❗️ 해결 : 최초의 터미널에서 “vi”를 입력하면, vi mode라는 상태를 감지하고, vi mode 전용 키매핑을 진행하였다.

 

socket.io

🤔 문제 1 : front-back socket.io 통신간 CORS Error 발생

🙋‍♂️ 원인 : front와 back의 IP, 포트가 달라서 생기는 문제였다.

❗️ 해결 : socket 인스턴스 생성 시 cors처리를 위한 속성을 추가하여, Error를 해결하였다.

 const io = new Server(httpServer, {
  cors: {
    origin: true,
    credentials: true,
  },
})

 

 

🤔 문제 2 : 터미널 컴포넌트를 랜더 할 때, 클라이언트 측에서 데이터를 3번 보내는 문제가 발생했다.

🙋‍♂️ 원인 : React 특성상, 상태가 변하면 재렌더링되기 때문에, 초기 상태부터 현재 상태까지 3번 랜더가 된다.

❗️ 해결 : 받은 데이터를 처리할 때, 조건을 만들어, 어떠한 조건에 해당이 되는 명령어만 데이터를 처리하여 최종적으로 3번 랜더가 되더라도, 조건에 맞는 명령어만 실행이 되게 하였다.

 

 

🤔 문제 3 : 터미널 컴포넌트를 랜더할 때마다, 연결 상태인 웹소켓이 계속 증가하면서 열렸다.

🙋‍♂️ 원인 : 네트워크 탭에 활성화 상태인 Websocket이 계속 열려있는 상태로, 새로운 터미널이 열릴 때마다 추가로 계속 열렸다.

❗️ 해결 : 터미널 컴포넌트를 벗어나면, return문으로 소켓을 종료하고, socket 객체를 생성하는 코드를 최상위로 올려서 처음 랜더링 시 웹소켓을 연결하여, 추가적인 웹소켓 생성을 막았다.

 

 

🤔 문제 4 : 전체 채팅방처럼 여러 사람의 명령어 및 출력내용이 동시에 보이는 문제

🙋‍♂️ 원인 : 소켓을 열 때, 모든 사용자가 같은 소켓으로 접속하는 상황이었다.

❗️ 해결 : 소켓마다 터미널 객체를 각각 부여하여서, 각 소켓이 다른 터미널을 열 수 있도록 하였다.

 

 

React

🤔 문제 1 : 새로 고침시 404 발생

🙋‍♂️ 원인 : 새로고침시 현재 URL을 기준으로 요청을 하는 과정으로 인해 404 에러가 발생하였다.

❗️해결 : Nginx에서 404 발생 시 index.html을 보여주도록 변경하였다.

location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                #try_files $uri $uri/ index.html;
                if ( !-e $request_filename ) {
                        rewrite ^(.*)$ /index.html break;
                }
        }
							# 요청 파일이 존재하지 않으면 url을 index.html로 다시 작성해서 전달 

 

 

🤔 문제 2 : Command 상태를 선언했는데, command를 Back server에 전달했지만 계속 빈 값이 들어온다.

🙋‍♂️ 원인 : terminal객체가 없을 때 terminal을 생성하는 조건 때문에 초기 상태값이 지정되어 있는 상태로 실행되기 때문에, 최초 렌더시 상태가 불러와진다.

❗️해결 : terminal 안에 변수 하나를 선언하여, 누른 키를 변수에 담아두고, 변수를 back server로 전달한다.

 

 

🤔 문제 3 : GitHub Actions를 통해 배포 시 빌드 과정에서 에러가 발생함

🙋‍♂️ 원인 : React를 빌드하는 과정에서 ESLint 규칙에 의해 사용하지 않은 변수는 에러를 발생하지만, github actions는 프로덕션모드로 빌드를 진행하기 때문에 에러, 경고 등 감지하고 빌드 진행을 중지한다.

❗️ 해결 : ESLint 규칙 설정을 통해서 사용하지 않은 변수에 대해서 경고를 발생하지 않도록 한다.

{
    "extends": ["react-app"],
    "rules": {
        "react-hooks/exhaustive-deps": "off",
        "no-unused-vars": "off"
    }
}

 

 

 

Back-End

🤔 문제 1 : npm install시 node-pty 관련 패키지 설치 불가

🙋‍♂️ 원인 : Node-pty가 최신 Node를 지원하지 않았다.

❗️ 해결 : Node lts 버전을 이용하여 해결하였다.

 

🤔 문제 2 : 간헐적으로 지정해 놓은 Shell Script가 정상적으로 작동하지 않음

🙋‍♂️ 원인 : 페이지가 이동할 때마다 현재 모드를 판단해서 script를 실행했지만, 이전 페이지가 MySQL 모드인 상태에서는 exit를 하지 못하고, Shell Script 가 실행됨.

❗️해결 : 이전 모드와, 현재 모드를 판단하여, 이전 페이지가 MySQL모드 일 경우 exit 명령어를 먼저 작성하고, 실행할 수 있도록 하였다.

 

 

🤔 문제 3 : 웹 소켓 Connection 오류 발생

🙋‍♂️ 원인 : https로 서버를 배포하는 과정에서 .io로 들어오는 요청에 대해서 처리하지 못했다.

❗️ 해결 : .io로 들어오는 요청에 대한 라우팅을 Nginx 설정에 지정했다.

location ~* \\.io {
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_set_header X-NginX-Proxy false;

      proxy_pass <http://localhost:3005>;
      proxy_redirect off;

      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
    }

 

 

🤔 문제 4 : SQL문제에서 띄어쓰기가 있는 command는 정답처리 및 Next 버튼이 작동하지 않음

🙋‍♂️ 원인 : URL의 Params에 있는 command를 이용하여 정답요청 및 버튼 동작 핸들러 코드를 이용하다 보니, 띄어쓰기를 이스케이프 코드(%20)로 판단하여, command가 원하는 대로 불러와지지 않았다.

❗️ 해결 : url에서 command를 불러오는 과정에서 .indexOf 와 replace를 이용하여 %20을 띄어쓰기로 바꾸는 과정을 추가했다.

 

 


 

'시작 > TIL(Today I Learned)' 카테고리의 다른 글

230418 - TypeScript 기초  (0) 2023.04.19
230417 - BlockChain, BitCoin 기초  (0) 2023.04.18
230320 - AWS 사용자  (0) 2023.03.21
230316 - React - Redux-persist, 중첩라우팅  (0) 2023.03.16
230315 - React - Redux middleware  (0) 2023.03.15

댓글