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는 클라이언트로 돌려 보낼 응답 관련 여러 처리를 해줄 수 있는 객체다. 매개 변수의 순서가 중요하지, 이름은 개발자의 편의에 따라 커스텀할 수 있다.
    • 원하는 라우팅 경로에 따라 다른 로직 처리를 해서 값을 반환해주고 싶다면 appget 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.paramsreq.query는 각각 URL에 있는 path 정보와 query 정보를 캐치하는 데 사용된다. 둘 다 객체를 반환한다.
    • app.get('/article/:id', (req, res) => res.send(req.params.id))라고 작성하면 /article/ path뒤에 오는 값들이id라는 변수에 할당되며,req.params{ id: ${입력된 값} }형태로 값을 갖게 된다. 그래서req.params.idres.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 디렉토리를 별도로 생성해줘야 한다.
    • viewshome.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.useapp.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에 순차적으로 추가할 수 있게 구현해준다.
    • 구현한 내용에 대해선 별도 블로그 포스팅으로 정리해서 남길 예정이다.