* 프론트컨트롤러 v3 구현
- 기존의 단점 : 컨트롤러에 req, res등 서블릿 기술에 의존적임.
- 단점2 : 아래와같이 viewPath부분의 중복이 많음.
public process(req : Request , res : Response) : MyView {
const viewPath : string = path.join(process.cwd(), 'dist','views','members.html');
return new MyView(viewPath);
}
- ModelView
- 여기의 map으로 new-form 등을 매칭해줄거임.
- model에 < 뷰의 논리이름, 실제객체 >를 넣어준다!!
- 여기의 map으로 new-form 등을 매칭해줄거임.
export class ModelView {
private viewName : string;
private model : Map<String, Object>= new Map();
constructor(viewName : string) {
this.viewName = viewName;
}
public getViewName(){
return this.viewName;
}
public setViewName(viewName : string): void {
this.viewName = viewName;
}
public getModel() : Map<String, Object>{
return this.model;
}
public setModel(model : Map<String, Object>): void{
this.model = model;
}
}
- 컨트롤러 설계
- ModelView를 반환해준다.
import {ModelView} from "../ModelView";
export interface ControllerV3{
process (paramMap : Map<String, Object>) : ModelView;
}
- 컨트롤러 구현
- 논리적인 이름만 넘긴다.
- 물리적이름 만들기는 프론트컨트롤러의 viewResolver에서 담당한다.
import {ControllerV3} from "../ControllerV3";
import {ModelView} from "../../ModelView";
export class MemberFormControllerV3 implements ControllerV3{
process(paramMap: Map<String, Object>): ModelView {
return new ModelView("new-form");
}
}
- model에 < 뷰의 논리이름, 실제객체 >를 넣어준다!!
import {ControllerV3} from "../ControllerV3";
import {ModelView} from "../../ModelView";
interface Member {
id: number;
loginId: string;
name: string;
}
export class MemberListControllerV3 implements ControllerV3{
process(paramMap: Map<String, Object>): ModelView {
//todo : repository에서 member 모두 찾아서 넣기
const members: Member[] = [
{ id: 1, loginId: 'user1', name: '홍길동' },
{ id: 2, loginId: 'user2', name: '김철수' },
{ id: 3, loginId: 'user3', name: '이영희' }
];
const mv : ModelView = new ModelView("members");
mv.getModel().set("members",members);
return mv;
}
}
- 버그수정
- 버그 : viewsmembers 이런식으로 / 가 생략되어 파일을 못찾음
- path 모듈이용해서 수정
/**
* 논리이름을 (members)
* 물리이름으로 변환 (~~~/dist/views/members.html)
* @param viewName
* @private
*/
private viewResolver(viewName: string):MyView {
const viewPath: string = path.join(process.cwd(), 'dist', 'views',viewName+'.html');
const view = new MyView(viewPath);
return view;
}
* 동적렌더링 구현
- 현재는 그냥 html을 그대로 그려주고있다.
- 이를 동적으로 바꾸고자 한다.
<div class="container">
<h1>회원 목록</h1>
<table>
<thead>
<tr>
<th>ID</th>
<th>로그인 ID</th>
<th>이름</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>chovy</td>
<td>정지훈</td>
</tr>
<tr>
<td>2</td>
<td>faker</td>
<td>이상혁</td>
</tr>
<tr>
<td>3</td>
<td>tsla</td>
<td>일론머스크</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
- map.forEach 주의점
- val, key순으로 callback을 조회해야한다.
- 보통 key, value 순으로 생각했는데 특이하다??
- 뭔가 크롬에서 html이 렌더링되지않고 파일이 다운로드 되버리는 버그가 있다.
- 캐시 삭제, 강력 새로고침하니 해결되었다.
- 현재 데이터를 강제로 넣어준경우 동적렌더링은 작동한다.
// 사용 예시
const pageData = {
title: '회원 목록',
members: [
{ id: 1, loginId: 'user1', name: '홍길동' },
{ id: 2, loginId: 'user2', name: '김철수' },
{ id: 3, loginId: 'user3', name: '이영희' }
]
};
const renderedHtml = await this.renderEjsTemplate(viewPath, pageData);
- 이제 할일은 data를 pageData의 형태로 바꿔주는 일이다.
- 하려고봤더니 또 버그가생겼다.
- 바로 ejs는 내용이 변하지 않는다는 점이다.
- 그래서 내용을 바꿔도 ejs의 해시값은 똑같기때문에 , 304 Not Modified가 되면서 과거의 내용이 보여진다.
- 일단 임시로 etag 기능을 삭제한다.
- 템플릿엔진의경우, etag나 last-modified 기능을 못쓰는것인지? 의문이다
- 이제 진짜 할일은 data를 pageData의 형태로 바꿔주는 일이다.
- 먼저 jsp의 동작방식을 살펴보자.
- render 함수에서 model을 다시 request로바꾼후 속성을 set해줘야 jsp에서 접근이 가능한 구조다.
- 이부분을 좀 수정해야 될것같다.
- model에는 현재 < "members" , member 객체 > 가 담겨있다.
- 이 모델을 객체로 바꿔서 넘기면 될것같다.
public async renderEjs(model: Map<String, Object>, req : Request, res : Response) : Promise<void> {
let pageData = {};
model.forEach((val, key) =>{
pageData = { ...pageData, val};
});
await res.forwardEjs(req,res,this.viewPath, pageData);
}
* map to obj
- 일단 String이 아니라 string으로 Map을 바꿔준다.
- map < members , member 객체 > 를 obj로 바꿔주고 넘기면 된다.
- obj[key] = value; 이런식으로 값을 set 할수있다.
mapToObject(map: Map<string, object>): { [key: string]: object } {
const obj: { [key: string]: object } = {};
for (const [key, value] of map) {
obj[key] = value;
}
return obj;
}
public async renderEjs(model: Map<string, Object>, req : Request, res : Response) : Promise<void> {
const pageData = this.mapToObject(model);
await res.forwardEjs(req,res,this.viewPath, pageData);
}
- 이제 각각의 컨트롤러에서 ModelAndView에 절대경로, 모델객체만 잘 넘겨주면 알아서 렌더링된다.
- 이제 개발자는 비즈니스로직에만 집중할 수 있다.
'JS > boostCamp' 카테고리의 다른 글
24.10.5~7. 개발일지 // 회원가입구현, redirect, cookie, session, 인증 (0) | 2024.10.07 |
---|---|
24. 10. 4. 개발일지 // 정적서빙버그 fix (2) | 2024.10.05 |
24.10.3. 개발일지 // MemberSaveController 구현, 프론트컨트롤러 v4구현, 유연한 컨트롤러 구현, 어댑터패턴, instanceof interface (0) | 2024.10.03 |
24. 10. 1. 개발일지 // 미들웨어, 프론트 컨트롤러, static serving, 동적렌더링 (0) | 2024.10.01 |
24.9.30. 개발일지 // ts설정, favicon, view to dist, 조건부 요청 (0) | 2024.09.30 |