TIL
241219 TIL
GoJay
2024. 12. 19. 21:42
Udemy <The Web Developer 부트캠프 2024>
- Express는 Node 런타임을 이용해 서버 구축을 좀 더 수월하게 할 수 있도록 도와주는 프레임워크다.
- Node 환경에서
require('express')
로express
함수를 불러오고,express()
로 함수를 호출해주면 프레임워크에 해당하는 객체가 반환된다. 보통은app
이라는 이름으로 값을 받아서 사용한다(아래에선const app = express()
로 값을 받아왔다는 전제로, Express 객체를app
이라고 지칭하겠다). express
함수(app
)가 반환하는 객체에는 서버의 처리를 위한 다양한 값과 메서드들이 있다. 해당 프로퍼티들을 사용해서 원하는 서버 로직을 처리할 수 있다.app.listen
을 사용하면 서버를 실행하고, 원하는 포트를 열 수 있다. 로컬에서 서버를 띄울 땐 보통 포트 번호를3000
번으로 많이 설정하는데,app.listen(3000, {콜백 함수})
형태로 사용한다.app.listen
의 두 번째 인자로 오는 콜백 함수에는 처음 서버가 열릴 때 실행될 로직을 전달할 수 있다.- 라우트 경로에 상관 없이, 서버 접근에 대한 동일한 응답을 뱉어주고 싶다면
app.use()
메서드를 사용하면 된다. 매개 변수로는 하나의 콜백 함수를 받고, 콜백 함수에는 자동으로 매개 변수(req, res)
가 들어온다.req
는 클라이언트에서 온 요청 관련 정보를 담고 있는 객체고,res
는 클라이언트로 돌려 보낼 응답 관련 여러 처리를 해줄 수 있는 객체다. 매개 변수의 순서가 중요하지, 이름은 개발자의 편의에 따라 커스텀할 수 있다. - 원하는 라우팅 경로에 따라 다른 로직 처리를 해서 값을 반환해주고 싶다면
app
의get
post
patch
put
delete
등의 메서드를 사용하면 된다. 이름에서 알 수 있듯이, HTTP 메서드 종류는app
객체의 메서드로 구분해서 요청을 받는다. - 각 메서드들의 첫 번째 인자로는 원하는 라우팅 경로를 작성한다. 예를 들어,
app.get('/home', {콜백 함수})
형태로 사용하면 서버 경로의/home
path로 요청이 올 경우에 대해 대응한다.app.post('/home', {콜백 함수})
는home
경로로 온 POST 요청에 대응한다. - 라우팅 경로에
*
를 사용하면 모든 경로를 의미한다. Node는 파일에 작성한 라우팅 처리 로직을 위에서부터 순서대로 실행하기 때문에,*
경로에 대한 라우팅 처리 로직을 파일의 가장 앞쪽에 작성했다면 모든 접근에 대해서 해당 콜백 함수로 처리가 되니 주의하자. - 라우팅 경로의 요청을 받았으면, 콜백 함수로 필요한 로직 처리를 해줄 수 있다.
get
post
patch
put
delete
등 메서드는app.use
메서드의 콜백과 마찬가지로 각각 요청 정보와 응답을 위한req
,res
객체를 매개 변수로 받는다. res.send
메서드를 사용하면 매개 변수로 넘긴 값을 클라이언트에 반환할 수 있다. 반환할 수 있는 값은 문자열, 객체, HTMLElement 등 다양하며, 종류에 따라 http 요청 headers에 있는Content-type
의 값이 달라진다. 예를 들어서,res.send('WELCOME')
이라고 하면 헤더에"Content-type": "html/tex"
가 박혀서 응답되고,WELCOME
이란 문자열은 DOM의body
태그에 바로 박히게 된다.res.send({ styles: { color: 'red' }})
이런 식으로 객체를 응답하면"Content-type": "application/json"
이 헤더에 박혀서 응답이 날아간다.- 응답의 형태에 따라 클라이언트에서 받아서 사용할 수 있는 값의 형태가 달라지기 때문에, 타입의 정의나 형식을 잘 신경써야겠다.
req.params
와req.query
는 각각 URL에 있는 path 정보와 query 정보를 캐치하는 데 사용된다. 둘 다 객체를 반환한다.app.get('/article/:id', (req, res) => res.send(req.params.id))
라고 작성하면/article/
path뒤에 오는 값들이
id라는 변수에 할당되며,
req.params는
{ id: ${입력된 값} }형태로 값을 갖게 된다. 그래서
req.params.id를
res.send`로 클라이언트로 값을 내려주면 URL의 params 값에 따라 다른 응답을 처리해줄 수 있다.req.query
는 사용자가 URL에?{key}={value}
형태로 보내준 쿼리 메시지를{ key: value}
형태의 객체로 받아준다. 쿼리를 잘 사용하면 클라이언트에서 보낸 요청을 캐치해서 데이터베이스에 값을 저장하거나, 정렬 기준을 잡거나, 기타 등등 필요한 정보를 받아오는 데 유용하다.- 기본적으로 Express 서버를 실행할 땐 터미널로 실행할 모듈의 경로에서
node index.js
처럼 파일을 실행해주면 된다. 그런데, 한 번 서버를 실행하면 코드에 수정이 있더라도 수정 사항이 곧바로 반영되지 않고, 반영하려면 서버를 껐다가(터미널에서 ctrl+c 입력) 다시 띄워줘야 한다. - nodemon 패키지를 사용하면 수정 사항을 반영하기 위해 서버를 껐다 킬 필요 없이 수정 사항이 자동으로 반영되게 할 수 있다.
npm i -g nodemon
으로 nodemon 설치 후nodemon {실행할 파일}
로 서버를 띄워주면 된다. - 서버에서 HTML 템플릿 파일을 내려줘야 할 때 ejs 패키지를 사용할 수 있다. ejs는 HTML Document 문서에 일부 필요한 자바스크립트 코드를 작성할 수 있도록 해주는 문법으로, 서버에서 HTML 템플릿을 생성한 후 자바스크립트 코드로 동적인 일부 처리를 해주고 싶을 때 사용한다.
- ejs를 사용하려면 먼저
npm i ejs
로 ejs 패키지를 설치해줘야 한다. - ejs를 사용하기 위해선
app.set('view engine', 'ejs')
로 HTML 템플릿 시 ejs 패키지를 사용한다는 것을 명시해줘야 한다. - 그리고, ejs는 프로젝트 디렉터리에 있는
views
디렉터리에서{파일명}.ejs
확장자로 된 파일의 뷰를 참조한다. 따라서, ejs를 사용하려면 프로젝트의 루트에views
디렉토리를 별도로 생성해줘야 한다. views
에home.ejs
와 같이 템플릿을 만들고, 필요한 HTML 코드를 넣어준 다음에, 상위에 있는index.js
모듈의 라우팅 요청 처리 콜백 함수에서res.render('home')
형태로 사용하면 사전에 서버에서 정의한 템플릿을 클라이언트에 그대로 내려줄 수 있다.- ejs는 기본 설정 상 프로젝트 루트 디렉토리에 있는
views
디렉토리를 바라보고 있기 때문에,app.render
에는 경로 전체가 아니라views
디렉토리에서 참조하고 싶은ejs
모듈 이름을 작성하면 된다.app.set('view engine', 'ejs')
로 사전에 설정해줬기 때문에 확장자도 생략 가능하다. - 어떤 경로에서 nodemon을 실행하더라도
index.js
가 있는 폴더에서views
디렉토리를 바라볼 수 있도록 설정하려면app.set('views', path.join(__dirname, '/views'))
형태로 사용하면 된다. 이렇게 설정해두면 어디서 서버가 실행됐든 상관 없이views
디렉토리를 참조하는 경로를 고정할 수 있다. - 자바스크립트의 템플릿 문자열을 사용하면 문자열 안에서 자바스크립트 표현식을 사용할 수 있는 것처럼, ejs도 HTML 구문 내에 자바스크립트 코드를 작성할 수 있도록 한 태그가 있다.
<%= %>
처럼 사용하면 원하는 자바스크립트 표현식을 넣어서 평가된 값을 템플릿에 같이 포함시켜줄 수 있다. res.render('random')
처럼 특정 경로를 설정해서views/ejs
를 가져올 때, 두번째 인자로 객체를 전달하면 ejs 템플릿에 변수를 내려줄 수 있다. 예를 들어 ,res.render('random', { rand: num })
이라고index.js
모듈에 정의해주면random.ejs
모듈에서rand
라는 변수를 참조할 수 있게 된다.<%= %>
를 사용하면 감싸진 자바스크립트 표현식이 평가돼서 템플릿에 표현되지만,<% %>
형태로=
를 빼고 사용해주면 실제 view에 표시되진 않지만 자바스크립트 표현식을 평가해서 실행할 순 있다. 조건문, 반복문 등의 자바스크립트 구문은 실제 view로 표현하고 싶진 않지만, 로직 처리를 위해 사용이 필요한 경우가 있다. 이런 경우<% %>
형태로 태그를 사용한다.- 템플릿에 ejs 태그를 잘 사용하면 데이터 베이스나 별도 JSON에 저장된 데이터를 잘 활용해서 하나의 웹 페이지를 잘 구축할 수 있다.
- 템플릿에 필요한 static 파일들(e.g. CSS 파일, 이미지 등)은
express
의 정적 메서드인express.static
을 통해 참조할 수 있다. 모든 라우트 경로에서 동일한 로직을 실행할 때 쓰는app.use
에app.use(express.static('public')
같은 식으로 공개하고 싶은 정적 파일들이 있는 디렉토리(예시 경우에는public
)를 참조해주면 모든 페이지에서 참조한 디렉토리에 있는 정적 파일들을 활용할 수 있다. express.static
의 인자도 서버를 실행한 위치에 상관 없이index.js
가 있는 경로를 기준으로 정적 파일의 경로를 잡아주고 싶다면app.use(express.static(path.join(__dirname, '/public')))
과 같은 형식으로 사용해주면 된다.<%- %>
를 사용하면 이스케이핑하지 않고 태그 내부에 있는 값을 그대로 화면에 출력한다. 대표적으로 태그 내에 HTML 구문을 넣어주고 싶을 때 사용한다.<%- include({템플릿 파일 경로}) %>
형식으로 경로를 넣어주면 경로에 있는 템플릿 파일을 불러와서 넣어줄 수 있다. 마치 리액트에서 컴포넌트를 나눠놓고, 컴포넌트들을 필요한 곳에서 불러와 합치는 것과 유사하다.- 다만,
<%- include() %>
구문이 리액트의 컴포넌트와 다른 건, 템플릿에 박혀있는 HTML 구문을 정말 그대로 떼고 붙이고 한다는 것이다. 예를 들어서 페이지 상단에 있는head
태그 내부 메타 데이터의 HTML 코드를 별도 템플릿에 정의해놓고, 다른 템플릿에서head
위치에 해당 템플릿을 불러오면 마치head
를 변수에 저장했다 값을 가져오는 것처럼 치환된다.
React
render
함수 구현하기- 리액트에서 Virtual DOM 객체를 실제 DOM에 반영해주는
render
함수를 직접 구현해봤다. render
함수는 Virtual DOM 객체를 깊이에 따라 순회하면서 DOM 요소를 만들어주고, props들을 attribute로 등록해주고, children을 재귀적으로 탐색하면서 실제 DOM Tree를 만들어준다. 그리고 root 노드가 되는div#root
에 연결해준다.props
의 종류에 따라 예외 처리가 필요한 것들이 있다. 예를 들어key
는 실제 DOM에는 반영하지 않는 속성이고,styles
는 값을 객체로 받아 인라인 스타일로 실제 스타일을 먹여줘야 한다.props
에 이벤트 핸들러 함수가 들어오면addEventHandler
등을 사용해서 이벤트 핸들러로 부착해줘야 한다.- Fragment도 별도로 처리해서, 등장 시
children
요소들을 DOM에 순차적으로 추가할 수 있게 구현해준다. - 구현한 내용에 대해선 별도 블로그 포스팅으로 정리해서 남길 예정이다.
- 리액트에서 Virtual DOM 객체를 실제 DOM에 반영해주는