유데미에서 듣고있는 web developer bootcamp2024의 섹션34(358강)에서 더 복잡한 subreddit프로젝트를 만드는게 있길래 모범답안을 보기전에 먼저 만들기로 했다. 물론 엉망이긴 했다
https://github.com/woominKO/subreddit2
우선 데이터베이스를 연결하는 법은 알려주시지 않았기때문에 json형식으로 데이터를 주셨는데 이것때문에도 꽤나 힘들었다. json을 js에서 어떻게 파싱하는지도 몰랐기 때문이다.
gpt는 최대한 쓰지 않기로 했다. gpt는 정답이 하나인것처럼 말해주는데 그렇게 계속하다보면 생각하는 능력이 떨어지고 다양한 방법을 생각하고 그속에서 의사결정 하는 능력이 떨어질것 같다.
(그렇지만 마지막에는 써버렸다...)
아무튼 json파일은 이렇다
{
"soccer": {
"name": "Soccer",
"subscribers": 800000,
"description": "The football subreddit. News, results and discussion about the beautiful game.",
"posts": [
{
"title": "Marten de Roon to make pizza for more than 1,000 people in Bergamo if Atalanta win the Champions league.",
"author": "joeextreme"
},
{
"title": "Stephan Lichtsteiner has retired from professional football",
"author": "odd person"
},
{
"title": "OFFICIAL: Dani Parejo signs for Villareal.",
"author": "joeextreme"
}
]
},
"chickens": {
"name": "Chickens",
"subscribers": 23956,
"description": "A place to post your photos, video and questions about chickens!",
"posts": [
{
"title": "Naughty chicken hid under a shed for 3 weeks and came home with 25 chicks today!",
"author": "joeextreme",
"img": "https://preview.redd.it/pcn8u4j7c9z61.jpg?width=960&crop=smart&auto=webp&s=e114976dde1108b9556555d2db36a3cb6211798d"
},
{
"title": "Had to kill my first chicken today. Does it get any easier?",
"author": "sad boi"
},
{
"title": "My five year old chicken set and hatched one baby. I guess she wanted to be a mama one more time.",
"author": "tammythetiger",
"img": "https://preview.redd.it/lervkuis3me51.jpg?width=640&crop=smart&auto=webp&s=6a18ab3c4daa80eccf3449217589b922fa443946"
}
]
},
"mightyharvest": {
"name": "Mighty Harvest",
"subscribers": 44002,
"description": "Feeding many villages and village idiots for 10s of days.",
"posts": [
{
"title": "My first meyer lemon ripened today. Im so proud of the little guy. Banana for scale",
"author": "proudmamma",
"img": "https://preview.redd.it/1bz6we4j54941.jpg?width=640&crop=smart&auto=webp&s=a036ea99299f7737efde9f6c3bfa43893f5eaa00"
},
{
"title": "I think I overestimated the harvest basket size I needed.",
"author": "grower123",
"img": "https://preview.redd.it/4h99osd25i351.jpg?width=640&crop=smart&auto=webp&s=d651250a345bbceeba7a66632e8c52a02d71bc73"
}
]
}
}
구현하고 싶었던 것은
서버파일을 실행하고 url창에
localhost:3000/r/[subreddit주제]
를 입력하면 ejs가 주제에 맞춰서 페이지를 띄워줘야하는데
이때, ejs반복문을 사용해서 title,author를 출력하고 페이지의 이름과 description도 출력해야했다. css는 뒤로 미뤄두고 간단하게 글자들만 출력하기로 했다.
0.기본적인 express.js구조
1.라우팅(/r/서브레딧주제)
2.ejs 반복문을 어떻게 짤것인가
0.기본적인 express.js서버파일구조
0.1 express()해야하는 이유
0.2 app.listen반드시 써야함
1.프로젝트 경로에 있는 파일을 서버파일과 연결하는법
2.json파일을 js에 이식하는법
3.fs.readFile - (강사분은 이걸 안썼다..)
4.반복문기초
5.js 객체 접근문법
6. app.set이란?
7. res.render문법
8.path.join을 왜써야하고 어떻게 쓰는지
9.구조분해할당--> ex){...data}
10.res.params(이건 알았는데 자세히 공부해야할듯)
11.함수속 메소드 (express()와 express.static이 되는 이유)
나는 반복문을 어디까지 써야할지 고민이였다. 레딧 서브페이지는 따로 만들어야할지 그것조차 반복문(?)을 써야할지..
나는 subreddit마다 페이지를 따로따로 만들었지만
강사분은
const redditData = require('./data.json');
app.get('/r/:subreddit', (req, res) => {
const { subreddit } = req.params;
const data = redditData[subreddit];
if (data) {
res.render('subreddit', { ...data });
} else {
res.render('notfound', { subreddit })
}
})
이런식으로 :subreddit요청이 오면 json에서 아예 해당 subreddit만 추출해서 ejs에 보내고
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= name %> </title>
<link rel="stylesheet" href="/app.css">
</head>
<body>
<h1>Browsing The <%= name %> subreddit</h1>
<h2><%= description %></h2>
<p><%=subscribers %> Total Subscribers</p>
<hr>
<% for(let post of posts) { %>
<article>
<p><%= post.title %> - <b><%=post.author %></b></p>
<% if(post.img) { %>
<img src="<%= post.img %>" alt="">
<% } %>
</article>
<% } %>
</body>
</html>
이런식으로 만들었는데 놀라웠다.. 이생각을 왜못했지..
0.1 익스프레스를 사용할때
const express = require('express');
const app = express();
왜 이렇게 하는지 몰라서 index.js파일 만들고 어떤 코드부터 넣어야할지 머릿속에서 아무생각이 없이 멍~했다.
첫번째 줄에서 require로 모듈시스템을 이용하여 라이브러리를 불러오는것은 알게되었는데
두번째줄 관련 코드를 검색해보니 인스턴스를 생성하는코드라는데 인스턴스는 뭘까..
자바스크립트에서 인스턴스는 특정 클래스 또는 생성자함수로부터 생성된 객체를 의미합니다
function Person(name, age) { this.name = name; this.age = age; this.greet = function () { console.log(`Hello, my name is ${this.name}`); }; } // 인스턴스 생성 const person1 = new Person('Alice', 25); const person2 = new Person('Bob', 30); person1.greet(); // Hello, my name is Alice person2.greet(); // Hello, my name is Bob
위 예제에서 Person은 생성자 함수이고, person1과 person2는 Person의 인스턴스입니다.
class Animal { constructor(type, sound) { this.type = type; this.sound = sound; } makeSound() { console.log(`${this.type} says ${this.sound}`); } } // 인스턴스 생성 const dog = new Animal('Dog', 'Woof'); const cat = new Animal('Cat', 'Meow'); dog.makeSound(); // Dog says Woof cat.makeSound(); // Cat says Meow
여기서 Animal은 클래스이고, dog와 cat은 해당 클래스의 인스턴스입니다.
this.name = name은 생성한 인스턴스 괄호속에 있는 인자를 인스턴스의 메소드로 집어넣는다는 뜻인것 같다. 정확히 말하자면 생성한 인스턴스의 property에 괄호 속에 인자를 할당한다는 뜻이라고 한다.
그래서 익스프레스의 인스턴스를 만드는건 뭘까
내생각에는 app.get같은걸 쓰기 위해서 쓰는것 같긴하다.
require('express'):
Node.js의 모듈 시스템을 사용하여 Express 라이브러리를 불러옵니다.
이때 반환되는 값은 함수(function) 또는 객체(object)입니다.
express 변수:
이 변수는 Express 프레임워크 자체를 의미하며, 서버 인스턴스를 생성할 수 있는 함수를 포함합니다.
어렴풋이 알것같은데.. 정확히는 이해는 안가는 느낌...
좋은 질문이에요! 😊 Express에서
express()
는 왜 함수처럼 호출되는지 설명드리겠습니다.1. 핵심 차이점: Express는 함수(Function)이고, avante는 객체(Object)
객체(Object)
class Car { constructor(brand, name, color) { this.brand = brand; this.name = name; this.color = color; } } const avante = new Car('hyundai', 'avante', 'blue');
avante
는 클래스(Car)의 인스턴스(객체)입니다.- 객체는 함수처럼 호출할 수 없습니다. →
avante()
는 오류 발생!Express의 경우: 함수(Function)
const express = require('express'); // express는 함수! const app = express(); // 함수 호출 - require('express') //모듈 자체가 함수(Function)를 반환합니다. express() 호출: - 이 함수는 새로운 **애플리케이션 인스턴스(객체)**를 생성하고 반환합니다. - 반환된 객체(`app`)는 서버를 설정하는 데 사용됩니다. 즉, Express 모듈 자체가 함수이기 때문에 **`express()`**처럼 호출할 수 있습니다.
2. Express는 함수지만 객체처럼 동작
내부 동작:
function express() { const app = Object.create(null); // 새로운 객체 생성 app.use = function () { /* 미들웨어 추가 */ }; app.get = function () { /* GET 라우트 설정 */ }; app.listen = function () { /* 서버 실행 */ }; return app; // 최종 객체 반환 }
express()
함수는 내부적으로 객체(app)를 생성하고 반환합니다.- 이 객체에는 메서드(
use
,get
,listen
)가 정의되어 있습니다.- 결과적으로
express()
는 함수처럼 호출되지만 반환값은 객체이므로, 아래와 같은 사용이 가능해집니다.const app = express(); // 새로운 객체(app) 반환 app.get('/', (req, res) => res.send('Hello World!')); // 객체 메서드 사용
3. 일반 객체도 함수처럼 만들 수 있음! (팩토리 패턴)
Express의 구조는 팩토리 패턴(Factory Pattern)을 사용한 예제입니다. 팩토리 패턴을 활용하면 일반 객체도 함수처럼 동작할 수 있습니다.
function createCar(brand, name, color) { return { brand: brand, name: name, color: color, drive() { console.log(`${this.name} is driving`); } }; } const avante = createCar('hyundai', 'avante', 'blue'); // 함수 호출 avante.drive(); // 'avante is driving'
- 이 예제에서
createCar()
는 함수이면서 객체를 반환합니다.- Express도 이와 유사하게 동작합니다.
4. 결론
express()
는 함수(Function)로 정의되어 있기 때문에 직접 호출할 수 있습니다.avante
는 객체(Object)이기 때문에 직접 호출할 수 없습니다. - Express는 팩토리 패턴을 이용해 함수 호출 시 객체를 반환하는 방식으로 동작합니다.- 필요에 따라 일반 객체도 팩토리 패턴을 사용하여 함수처럼 동작하게 만들 수 있습니다.
- 이해가 안 되는 부분이나 추가 궁금한 점이 있으면 언제든 물어봐 주세요! 😊
0.2 app.listen이란?
서버를 어느포트에서 실행한껀지를 정하는데 문법은
app.listen(포트번호,콜백함수) 로 되는데 보통 콜백함수에는 console.log("you're listening to 포트번호")이렇게 많이 쓰는듯 하다
1.우선 익스프레스 모듈을 js파일로 불러와야 하는데 이때 require를 쓴다.
NodeJS에서는 require 메서드를 통해 외부 모듈을 가져올수 있다.
require메서드는 node가 local object에 추가한 메서드로서 다음 과 같이 파라미터로 추가할 모듈의 파일 경로값을 받는다
require('파일경로');
만약 연결하는 모듈파일이 같은 파일안에 있다는 경로값의 시작은 ./이 됩니다.
npm으로 설치하거나 기본내장모듈은 파일경로에 이름만 단순히 입력해도 불러와진다.
깟땜gpt한테 물어본 결과
require('express')
만 입력해도 되는 이유는 Express.js가 Node.js의 npm(Node Package Manager) 패키지로 설치되기 때문입니다.1. npm 패키지 구조
Express.js는 npm을 통해 설치되며,
node_modules
폴더에 저장됩니다.
예를 들어, 다음 명령어로 설치할 수 있습니다.npm install express
2. 패키지 이름과 모듈 경로 매칭
- 설치된 Express.js는
node_modules/express
폴더에 위치합니다.require()
함수는 기본적으로node_modules
폴더를 검색합니다.- 따라서
require('express')
는node_modules/express/index.js
파일을 로드합니다.3. package.json의 "main" 필드 활용
node_modules/express
폴더 안의package.json
파일에는 다음과 같은 내용이 있습니다.{ "name": "express", "main": "index.js" }
- 여기서
"main": "index.js"
는 기본 진입점(entry point)을 정의합니다.- 따라서
require('express')
는 자동으로index.js
를 불러옵니다.4. 파일 경로를 명시하지 않아도 되는 이유
직접 파일 경로(예:
require('./express')
) 대신require('express')
를 사용해도 되는 이유는:
- npm 패키지는 전역 모듈 검색 경로에 등록됩니다.
- Node.js는 자동으로
node_modules
폴더를 찾습니다.express
패키지는 등록된 이름으로 직접 호출됩니다.정리
Express.js를 npm을 통해 설치하면, Node.js는 자동으로 모듈을 찾고 로드합니다.
따라서 파일 경로 대신 패키지 이름만 입력하면 쉽게 사용할 수 있습니다.
*참고로 module.exports를 해야 해당모듈을 다른파일에서도 불러올 수 있다.
라고한다..
https://velog.io/@kakasoo/Node%EC%9D%98%EB%AA%A8%EB%93%88%EC%8B%9C%EC%8A%A4%ED%85%9C%EA%B3%BCrequire
참고링크
2&3 json파일을 js에 이식하는법
내가쓴 방법은 fs.readFile을 씀으로써 js가 json을 받아들이도록 했다.
fs는 file system의 약자로 nodejs 내장모듈이다
https://nodejs.org/api/fs.html#fsreadfilepath-options-callback
그렇다고 합니다
object array반복문
foreach는 반복문이 아니라 메소드였다는 사실!!
js 객체접근문법
참고*
key값에 접근하는법
*참고 app.use
https://expressjs.com/en/guide/using-middleware.html
https://backend.turing.edu/module2/lessons/how_the_web_works_http
Router-lever middleware도 있다는데 다음에 알아보자
const express= require('express')
const path= require('path')
const app = express()
app.set("view engine", "ejs");
app.set("views", path.join(__dirname, 'views'))
app.get('/r/:subreddit',(req,res)=>{
const subreddit = req.params.subreddit;
console.log(subreddit)
res.render('index', {subreddit:subreddit})
})
app.listen(3000);
위 코드를 보면서 path.join에 대해 알아보자
path.join은 view engine과 관련해서 쓰였다.
그래서 view, view engine 부터 알아보자
view와 view engine은
app.set의 name칸에 들어가는 property다
app.set은 아까봤듯이 말그대로 어플리케이션의 세팅을해주는 메소드인데
위에있는 사진에서 있던view에 대한 설명을 가져오자면
A directory or an array of directories for the application's views
라고한다.
그리고 view engine에 대한 설명은
The default engine extension to use when omitted.
라고 한다.
공식문서에는
라고 나와있다.
To render template files, set the following application setting properties, in the default app.js created by the generator:
views, the directory where the template files are located. Eg: app.set('views', './views'). This defaults to the views directory in the application root directory.
view engine, the template engine to use. For example, to use the Pug template engine: app.set('view engine', 'pug').
세팅을 해야한다는거 보니 app.set으로 view, view engine을 설정해야하는것 같다.
위에 설명한 app.set('views', './views') 을 보면 뒤에 인자에 파일경로를 써야되는것 같은데 위에 js코드를 보면 여기에 path.join이 쓰였다.
https://expressjs.com/en/guide/using-template-engines.html
9.구조분해할당
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
10.res.params
11.prototype
https://developer.mozilla.org/ko/docs/Web/JavaScript/Inheritance_and_the_prototype_chain
const express= require('express')
const app = express()
express.XXX
뭔가 이상하다
express는 함수라 호출도 되는데
express.xxx처럼 메소드도 있다.
아마 prototype때문에 가능한것 같다.