나만의 아고라 스테이츠 만들기

Kim-DaHam·2023년 3월 9일
0

JavaScript

목록 보기
7/18
post-thumbnail

🔥 학습목표

  • javsScript로 간단한 CRUD를 구현한다.
  • (추가) 진짜 홈페이지 처럼 페이지를 라우팅 하자.



🟩 구현하기 전에

사실 직고

일단 이 글은... 정확히는 3월 11일 토요일 오후에 작성하고 있다. 왜냐면 그 전에는 설계하고 구현하느라 바빠서 중간 과정을 기록하기란 나에겐 무리였고, 큰 어려움이 닥치면 그때 해결방법을 기록하자! 라고 생각했었다.

그리고 그 어려움이 지금 닥쳤다.

적고자 하는 핵심은 어려움->해결과정 이지만, 그래도 해결책만 달랑 적힌 글을 읽으면

그래서 이게 뭐였지?

라고 할까봐 그냥 처음부터 끝까지 흐름을 다 적어본다.

그러니까 나의 목표는

CRUD가 되는 아고라 스테이츠를 한 페이지에 다 넣기보다, 진짜 실사용 될 것처럼 아예 '아고라 스테이츠 홈페이지'를 만들고 싶었다.

왜냐하면... 일단 <리액트를 사용하지 않고>

  1. 여러 html을 라우팅 해보고 싶었고,
  2. 컴포넌트 단위로 개발 해보고 싶었고,
  3. 그냥 js CRUD에 대한 공부는 어느정도 되어있는 상태여서... 제대로 만들어서 티스토리 미니프로젝트 란에 올리고 싶었다.

🟣 UI 설계하기

⬜ MainPage

⬜ MainBoard

약간 GitHub 처럼 만들고 싶어서 많이 참고했다. css에서도 border 색상이나 배경 색상을 많이 따왔다.

⬜ BoardDetail (축약 ver.)

이 부분은 현재 디스코드에서 사용하는 아고라 스테이츠의 생김새를 많이 차용했다. 특히 오른쪽에 답변이 주르륵 나오는 부분이 그렇다.

디스코드에서 왼편에는 질문 목록이 뜨고 오른편 답변 공간에 원글까지 같이 나오는데, 나는 두 개를 분리해보고 싶었다.

답변을 하면서도 중간중간 질문의 요지를 파악하기 위해 스크롤을 위로 올렸다 내리는 경우가 많은데, 이렇게 반반으로 화면을 나누어 질문을 계속 주시하면서 답글을 다는 게 포인트다.

⬜ BoardDetail (큰 ver.)

그럼에도 더 큰 화면으로 볼 수 있어야 하긴 해야해서 큰 버전도 만들었다. 축약 버전에서 가운데 화살표 방향으로 화면을 당기면 큰 버전으로 변하면 좋겠지만...

가능할진 모르겠다. 리액트 컴포넌트 단위로 만지면 충분히 가능한데 지금 리액트를 안 쓰고 만드는 게 포인트 아닌 포인트라.

스크롤은 무한 스크롤로 만든 다음 top으로 이동하는 스크롤 이벤트 버튼을 하나 달아줄 예정이다.



🔴 들어가기 전에 겪은 문제들(파일 링크)

궁금한 점

  1. 왜 MainPage.html 을 라이브 서버로 키면 link 된 css, js, 기타 파일들이 다 불러와 지는데,
  • Header.html 을 불러오면 거기 link 된 Header.css는 연결 안 되고, MainPage.html에 Header.css를 링크 해줘야 할까?

  • href로 MainBoard 로 이동하고 부터는 data.js 가 연결되지 않을까?



🔵 Express 서버 구축하기

이게 내가 일이 커진 이유다... 그냥 라이브 서버 킨 다음에 href로 링크 이동만 해주면 되는 줄 알았는데, 브라우저에서 html 파일을 불러오면 딱 그 html 만 가져오고, link 된 css나 js 파일은 동시에 불어오질 못 해서,

이렇게 됐다.

일단 그냥 내가 알고있는 (바닥에 겨우 고여있는) 지식을 긁어모아 정적 파일을 지정해주는 것까진 성공했는데 나중에 보기 편하기 위해 그것도 다 정리해보려 한다.

⬜ 폴더 구조

📁public
└ 📁 Components
└ 📁 Route
└ 각종 공통 css 파일들
└ 📁 images
📄 app.js
📄 server.js
📄 pakage.js

  • 📁 public : 정적 파일들을 모아두는 폴더.
  • app.js : express 서버 구축
  • server.js : pakage.json의 main 실행 파일. express 서버를 연결하고 라우팅.

⬜ 서버 연결하기

  • app.js
const express = require('express');
const cors = require('cors');
const path = require('path');

class App {
    constructor() {
        this.app = express();
        this.setStatic();
    }

    setStatic() {
        this.app.use('/', express.static(path.join(__dirname, '/public')));
    }

}

module.exports = new App().app;

일단 모든 파일들의 직접적인 접근이 가능하도록, 정적 파일을 모아둔 폴더인 public을 static 미들웨어 함수에 넣어주었다.

  • server.js
const http = require('http');
const app = require("./app");

// const mainPage = require('./routes/mainPage');
// const mainBoard = require('./routes/mainBoard');

app.get("/", (req, res)=> {
    res.sendFile(__dirname + '/public/Route/MainPage/MainPage.html');
})

app.get("/board", (req, res)=>{
    res.sendFile(__dirname + '/public/Route/MainBoard/MainBoard.html');
})

// app.use('/', mainPage);
// app.use('/board', mainBoard);

const server = http.createServer(app);
server.listen(3000, ()=>{
    console.log("run on Server http://localhost:3000");
});

그 후 get 메소드를 사용하여 라우팅 주소를 지정해 주었다.

이때 라우터를 따로 분리하면 코드 관리가 편하댔는데(주석 처리한 부분이 그 흔적)

이상하게 자꾸 경로 에러가 나서 (짜증나서) 일단 지금 주말 내에 빨리 만드는 게 문제니까 잠깐 접어두고 server.js 파일에 다 넣었다. 어차피 만들 페이지도 3~4개 뿐이라 복잡해 보이진 않을 거 같다.

(얼른 완성하고 다시 확인할거다)
(🎁 Express 라우팅

그럼 이제 주소창에 localhost:3000/을 입력하면 메인 페이지가 뜨고,
localhost:3000/board 를 입력하면 게시판 페이지가 뜬다.


⬜ 그럼 이제 파일 연결은?

내가 지금 글을 작성하는 이유다. 나중에 까먹을까봐 이거 먼저 적고자 velog를 켰다.

🔴 문제 발생

express 서버를 구축하고 정적파일들을 정의하기 전,

MainPage.html 에서 사용 할 MainPage.css 와 MainPage.js 를 연결할 때 아래와 같이 작성했다.

<head>
	<link rel="stylesheet" href="./MainPage.css">
</head>
<body>
	<script src="./MainPage.js"></script>
</body>

두 경로 모두 public/Route/MainPage/MainPage.html 를 기준으로 한 상대경로다. 세 파일 모두 MainPage 폴더에 들어가 있다는 거다.

그런데 웬걸, express 서버를 연결하고 정적파일을 지정했더니 MainPage.html을 기준으로 한 상대 경로는 브라우저가 불러오지 못 했다. 아래 사진은 MainPage.css 가 적용되지 못 한 화면이다.

대충 위와 같은 MIME 어쩌고 에러가 발생했다.

type을 지정해주지 않거나, 아예 경로가 틀렸을 때 발생하는 에러라고 한다.

나의 경우엔 type을 text/js 등으로 지정해줘도 MainPage.js를 불러오지 못 하고 (사실 HTML5 부터는 자바스크립트가 기본 언어로 지정되어 있기 때문에 딱히 적어줄 필요도 없다.)

이런 에러를 뱉었다.

자세히 읽어보면 경로 문제임을 알 수 있다.

나는 MainPage.html 에서 MainPage.js를 불러오니까 html 파일의 기준으로 상대경로를 입력한 건데, http://localhost:3000/MainPage.js 주소를 요청하는 불상사가 발생했다.


🔵 해결 방법

내가 브라우저에서 접근 할 정적 파일들을

this.app.use('/', express.static(path.join(__dirname, '/public')));

이렇게 지정해줬기 때문이다.


이제 MainPage.js 파일을 접근하려면

http://localhost:3000/Route/MainPage/MainPage.js

의 주소로 접근해야 하는 것이다.

그러려면 불러 올 css와 js 파일의 경로를

<head>
	<link rel="stylesheet" href="../../Route/MainPage/MainPage.css">
</head>
<body>
	<script src="../../Route/MainPage/MainPage.js"></script>
</body>

와 같이 작성해야 한다...


🟧 두 번째 문제점

두 번째 난관에 봉착했다.

html 문서를 컴포넌트화를 하기 위해 나는 html 문서 안에 html 문서를 넣었다.

🎁 참고 블로그 는 여기다.

그런데,

MainPage.html 에서 빨간색 표시한 부분을 RecentQuestion.html 문서로 따로 작성하여 컴포넌트화 했다. 최근 올라온 질문글을 목록으로 볼 수 있는 부분인데, 질문글을 불러오려면 RecentQuestion.js 파일을 불러와야 했다.

그런데 RecentQuestion.html 에서
<script src="../../Components/RecentQuestion/RecentQuestion.js"></script> 을 부르면 연결되지 않고,

MainPage.html 에서 부르면 연결 되는 거다.

생각해보니까 내가 html 안에 html 컴포넌트를 넣을 때


  • MainPage.js
async function fetchHtmlAsText(url) {
    return await (await fetch(url)).text();
}
async function importPage(target) {
    document.querySelector('#' + target).innerHTML = await fetchHtmlAsText('../../Components/' + target + '/' + target + '.html');
}

importPage('Header');
importPage('Footer');
importPage('RecentQuestion');

이렇게 불러왔는데, innerHTML은 <script> 태그를 실행시키지 않는다는 거였다.

난 바보다.

해결 방법은 세 가지가 있었다.


🔵 0. MainPage.html 에서 RecentQuestion.js 를 부른다.

이게 왜 0번이냐면 바보 같은 내 생각이기 때문이다. innerHTML을 사용하여 자칭 html 컴포넌트를 MainPage.html에 넣으면 외부 js에서 querySelector로 접근이 안 된다.

<script src="MainPage.js"></script> 에서 innerHTML로 (자칭)컴포넌트를 넣고,
<script src="RecentQuestion.js"></script>을 부르기 때문에

비동기 때문인가? 라고 생각했지만

console.log 로 출력해보면
<selector id="RecentQuestion"></selector>
이라고 뜨긴 뜬다. 자식 노드까지 다 갖춘. 그런데 그 자식 노드들에 querySelector로 접근이 안 된다. 왜!!!!!!!!!!!!!!


🔵 1. jQuery의 $(elemental).html(string)을 사용한다.

첫 번째 해결법인 jQuery는 내가 잘 쓸줄 모른다. 써보긴 했는데 지금 당장은 다 까먹었다... 그리고 코드가 뒤죽박죽 될 것 같은데다 지금 이거 얼른 끝내고 다른 것도 할 게 산더미인데...


🔵 2. script 객체를 직접 만들어 DOM에 집어넣는다. 그게 무슨 말이냐면,

const script = document.createElement("script"),
  text = document.createTextNode("console.log('foo')");
 
script.appendChild(text);
document.body.appendChild(script);

이렇게...

두 번째 해결법은 그냥 아, 싫다. 뭔가 저건 아니라는 생각이 든다.



그래서 구글에 'html 컴포넌트' 라고 검색해보니까 html의 컴포넌트화ㅊ라는 게 있었다.

그러니까 내가 지금 하고싶었던 걸 🎁 웹 컴포넌트 라고 부르고 있었다.

왜 진작 저렇게 검색 안 하고 html 안에 html~ 이런 것만 검색해서 엉뚱한 행동을 하고 있었지?


그래서 결론은,

나 이거 지금 못 해....

빨리 CRUD 구현해서 내일 스터디 발표해야 된단 말이야...
절망적이었지만 어쨌든 방법은 있다는 가능성을 등 뒤에 놔두고 얼른 결과물이나 만들기로 했다...
나 여태 뭐한건지 모르겠다... 시간낭비가 맞은 걸까 그냥 시키는 것만 할 걸 그랬나...
그래도 헛삽질 해서 '왜 안 되는가'를 깨달은 거에 후회는 없지만...
아니다 후회 되나...? 왜냐면 다른 사람들은 단번에 깨달을 걸 혼자 파악하기까지 이만큼 오래걸렸으니까...
대박이다 진짜...



그래서 일단 걍 컴포넌트고 뭐고 MainPage.html에 다 때려 박기로 했다.

🟩 페이지 단위

🟣 MainPage

⬜ MainPage.html

⬜ MainPage.css

⬜ MainPage.js



🟣 MainBoard

⬜ MainBoard.html

⬜ MainBoard.css

⬜ MainBoard.js



컴포넌트 단위

🟣 Header

⬜ Header.html

⬜ Header.css

⬜ Header.js

profile
다 하자

0개의 댓글