* 쿠키 파서 fix, MyCookieParser 구현
- 현재 cookie parser가 작동을 안한다.
- 라이브러리로 불러와서 내가 만든 was와 호환이 안되는것 같다.
- -> 직접 만들자.
- 역할 : cookie들을 파싱해서 req.cookies에 아래와 같은 식으로 넣어주면 되나?
- MyCookieParser
import {Request} from "../was/request";
import {Response} from "../was/response";
/**
* 쿠키 문자열을 파싱하여 객체로 변환합니다.
* @param cookieString 쿠키 문자열
* @returns 파싱된 쿠키 객체
* {
* acookie: "dfdkfokeoef",
* bcookie: "afkifkjeife",
* ccookie: "asdsd"
* }
*/
export function MyCookieParser(req : Request, res: Response, next) {
const cookies: { [key: string]: string } = {};
const cookieString = req.get('cookie');
// 쿠키 문자열을 세미콜론으로 분리
const cookiePairs = cookieString.split(';');
for (let pair of cookiePairs) {
// 각 쌍을 트림하고 '='로 분리
const [key, value] = pair.trim().split('=');
// 키와 값이 모두 존재하는 경우에만 객체에 추가
if (key && value) {
cookies[key] = value;
}
}
req.cookies = cookies;
next();
}
* db연결 ,typeorm 설정
- db연결, typeorm을 곁들인?
- 인프런이 typeorm 쓴다고 해서 typeorm 사용하고자 함
- 기업들이 많이 쓰는듯함
- 우선 .env 파일만들고 npm install dotenv
- main.ts에서 import
import "dotenv/config";
- type orm 쓰려면 new Datasource를 생성해서 써야함
- connection pool은 10개로 기본값설정됨.
import "reflect-metadata"
import { DataSource } from "typeorm"
import {Member} from "../domain/member/Member";
export const AppDataSource = new DataSource({
type: process.env.DB_TYPE as "mysql" | "postgres" | "sqlite" | "mariadb",
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT || "3306"),
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
synchronize: true,
logging: true,
entities: [Member],
migrations: [],
subscribers: [],
})
- 그리고 npm install mysql2 해야함
- 그리고 decorator 기반이기때문에 ts config 수정해야함
{
"compilerOptions": {
"module": "commonjs",
"outDir": "dist",
"sourceMap": true,
"target": "ES6",
"lib": ["es6"],
"experimentalDecorators": true,
"emitDecoratorMetadata": true
},
}
- 엔티티 수정
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";
@Entity("members")
export class Member {
@PrimaryGeneratedColumn()
private id: number; // DB의 AutoIncrement Id
@Column()
private nickname: string;
@Column()
private readonly password: string;
@Column({ unique: true })
private readonly email: string;
constructor(id: number, email : string , nickname: string, password : string) {
this.id = id;
this.nickname = nickname;
this.password = password;
this.email = email;
}
public setId(id: number) : void {
this.id = id;
}
public getEmail(){
return this.email;
}
public getId() {
return this.id;
}
public getPassword() {
return this.password;
}
}
- 메인에서 이렇게 사용하면 됨.
AppDataSource.initialize().
then(() => {
console.log("Data Source has been initialized!")
}).catch((err) => {
console.error("Error during Data Source initialization", err)
})
- 이제 memberRepository를 컨트롤러에서 주입받아서 사용?
- 먼저 AppDataSource를 싱글톤으로 해야 한다. -> 안하면 컨트롤러마다 호출하는곳에서 새로운 DataSource를 만들면서 error가 난다. 그때그때마다 new Datasource를 해줘야한다.
import "reflect-metadata"
import { DataSource } from "typeorm"
import {Member} from "../domain/member/Member";
export class AppDataSource {
private static instance: DataSource | null = null;
private constructor() {}
public static getInstance(): DataSource {
if (!AppDataSource.instance) {
AppDataSource.instance = new DataSource({
type: process.env.DB_TYPE as "mysql" | "postgres" | "sqlite" | "mariadb",
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT || "3306"),
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
synchronize: true,
logging: true,
entities: [Member],
migrations: [],
subscribers: [],
});
}
return AppDataSource.instance;
}
}
- memberRepository
import { Member } from "./Member";
import { AppDataSource } from "../../repositories/AppDataSource";
import { Repository } from "typeorm";
export class MemberRepository {
private static instance: MemberRepository | null = null;
private memberRepository: Repository<Member>;
private constructor() {
this.memberRepository = AppDataSource.getInstance().getRepository(Member);
}
/**
* MemberRepository의 유일한 인스턴스를 반환합니다.
* 인스턴스가 없으면 새로 생성하여 반환합니다.
* @returns {MemberRepository} MemberRepository의 유일한 인스턴스
*/
public static getInstance(): MemberRepository {
if (MemberRepository.instance === null) {
MemberRepository.instance = new MemberRepository();
}
return MemberRepository.instance;
}
/**
* 새로운 Member를 저장소에 추가합니다.
* @param {Member} member - 저장할 Member 객체
* @returns {Promise<Member>} 저장된 Member
*/
public async save(member: Member): Promise<Member> {
try {
return await this.memberRepository.save(member);
} catch (error) {
if (error.code === 'ER_DUP_ENTRY') {
console.log('이미 존재하는 email 입니다.' + member.getEmail());
throw new Error('이미 존재하는 email 입니다.' + member.getEmail());
}
throw error;
}
}
/**
* Email로 Member를 조회합니다.
* @param {string} email - 조회할 Member의 Email
* @returns {Promise<Member | null>} 조회된 Member 객체 또는 null
*/
public async findByEmail(email: string): Promise<Member | null> {
return await this.memberRepository.findOne({ where: { email } });
}
public async findById(id: number): Promise<Member | null> {
return await this.memberRepository.findOneBy({ id });
}
/**
* 모든 Member를 반환합니다.
* @returns {Promise<Member[]>} 저장된 모든 Member 객체의 배열
*/
public async findAll(): Promise<Member[]> {
return await this.memberRepository.find();
}
}
- 를 사용하는 컨트롤러 -> 추후에 로직이 복잡해지면 서비스 레이어로 분리
import {Member} from "../../../domain/member/Member";
import {MemoryMemberRepository} from "../../../domain/member/MemoryMemberRepository";
import {ControllerV4} from "../ControllerV4";
import {MemberRepository} from "../../../domain/member/MemberRepository";
import {AppDataSource} from "../../../repositories/AppDataSource";
export class MemberSaveControllerV4 implements ControllerV4{
private memberRepository: MemberRepository;
constructor() {
this.memberRepository = MemberRepository.getInstance();
}
process(paramMap: Map<string, string>, model: Map<string, object>):string {
const email: string = paramMap.get('email');
const nickname: string = paramMap.get('nickname');
const password: string = paramMap.get('password');
const member = new Member(email, nickname , password);
this.memberRepository.save(member);
model.set("member", member);
return "save-result";
}
}
* 에러 핸들 문제
- 현재 중복된 email로 가입하는경우, email 컬럼에 unique 제약조건으로 인해 error 가 난다.
- 이를 catch 해주는 부분이 없어서 main 까지 error 가 전파되서 서버가 죽는듯하다.
- 일단 임시로
- 일단 error를 더이상 던지지 않도록하여 임시적으로 해결.
/**
* 새로운 Member를 저장소에 추가합니다.
* @param {Member} member - 저장할 Member 객체
* @returns {Promise<Member>} 저장된 Member
*/
public async save(member: Member): Promise<Member> {
try {
return await this.memberRepository.save(member);
} catch (error) {
if (error.code === 'ER_DUP_ENTRY') {
console.log('이미 존재하는 email 입니다.' + member.getEmail());
// throw new Error('이미 존재하는 email 입니다.' + member.getEmail());
}
// throw error;
}
}
* login 구현 with db
- db에서 결과가 오기를 기다려야한다.
- await을 안붙이면 promise 객체가 되어 작동하지 않는다.
- async await 가 전파되는 문제... service함수를 호출하는 곳이 또있고 계속 호출 호출... 되서 100번 호출된다면 100곳에 모두 이작업을 반복해야 하는가?
- 수정문제 : 어떤 함수가 await 기능이 필요할수있으니 항상 async를 붙여 놓는게 나은가? 필요할때 그때그때 붙이는게 나은가?
* 회원리스트 구현 with db
- 멤버 엔티에 컬럼추가
@CreateDateColumn() //밀리초 저장안함.
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
- 컨트롤러의 저장소를 db저장소로 변경
- await 달기
import {ControllerV4} from "../ControllerV4";
import {MemoryMemberRepository} from "../../../domain/member/MemoryMemberRepository";
import {MemberRepository} from "../../../domain/member/MemberRepository";
export class UserListControllerV4 implements ControllerV4{
private memberRepository: MemberRepository = MemberRepository.getInstance();
async process(paramMap: Map<string, string>, model: Map<string, object>) {
const members = await this.memberRepository.findAll();
model.set("members",members);
return "user/list";
}
}
- 를 호출하는 adapter handle에도 await 달기
- 뭔가 정렬이 맘대로 안된다.
- 앞의 * 표시도 없애고 싶다.
ul {
list-style-type: none;
}
<section class="board-box">
<p class="board-count">전체멤버<span>N</span>명</p>
<ul class="board-list" >
<li class="board-header">
<p class="board-header-nickname BordS">닉네임</p>
<p class="board-header-email BordS">이메일</p>
<p class="board-header-createdAt BordS">회원가입일</p>
</li>
<% members.forEach(function(member) { %>
<li class="board-item">
<p class="board-header-nickname BordS"><%= member.nickname %></p>
<p class="board-header-email BordS"><%= member.email %></p>
<p class="board-header-createdAt BordS"><%= member.createdAt %></p>
</li>
<% }); %>
</ul>
</body>
ul {
list-style-type: none;
}
.board-list {
list-style-type: none;
padding: 0;
display: grid;
grid-template-columns: 1fr 2fr 1fr; /* 닉네임, 이메일, 회원가입일의 비율 */
gap: 10px; /* 열 사이의 간격 */
}
.board-header, .board-item {
display: contents;
}
.board-header-nickname,
.board-header-email,
.board-header-createdAt {
padding: 10px;
text-align: left;
}
.board-header p {
font-weight: bold;
background-color: #f0f0f0; /* 헤더 배경색 */
}
.board-item p {
background-color: #ffffff; /* 아이템 배경색 */
}
/* 짝수 번째 아이템에 다른 배경색 적용 (선택사항) */
.board-item:nth-child(even) p {
background-color: #f9f9f9;
}
/* 반응형 디자인을 위한 미디어 쿼리 */
@media (max-width: 768px) {
.board-list {
grid-template-columns: 1fr;
}
.board-header {
display: none; /* 모바일에서는 헤더를 숨김 */
}
.board-item p {
padding: 5px;
}
.board-item {
display: grid;
grid-template-columns: 1fr;
gap: 5px;
margin-bottom: 10px;
}
}
'JS > boostCamp' 카테고리의 다른 글
24. 10. 10. 개발일지 // 글쓰기 구현, 동적 url 매핑 , n+1 문제 (0) | 2024.10.10 |
---|---|
24. 10. 9. 개발일지 // redirectAttributes, 로그인이완료된후 원래페이지 이동, db, n+1 문제 (1) | 2024.10.10 |
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 |