* 쿠키 파서 fix, MyCookieParser 구현
현재 cookie parser가 작동을 안한다.
라이브러리로 불러와서 내가 만든 was와 호환이 안되는것 같다.
-> 직접 만들자.
역할 : cookie들을 파싱해서 req.cookies에 아래와 같은 식으로 넣어주면 되나?
이렇게 만드는 미들웨어를 만들자
import {Request} from "../was/request" ;
import {Response} from "../was/response" ;
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
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;
@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;
}
}
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);
}
public static getInstance(): MemberRepository {
if (MemberRepository.instance === null ) {
MemberRepository.instance = new MemberRepository();
}
return MemberRepository.instance;
}
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;
}
}
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 });
}
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를 더이상 던지지 않도록하여 임시적으로 해결.
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());
}
}
}
* login 구현 with db
이를 위해서 async , await을 붙여줘야한다.
이를 호출하는 부분에서도 async await 가 전파되야한다.
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 : 1 fr 2 fr 1 fr;
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 : 1 fr;
}
.board-header {
display : none;
}
.board-item p {
padding : 5px ;
}
.board-item {
display : grid;
grid-template-columns : 1 fr;
gap: 5px ;
margin-bottom : 10px ;
}
}
뭔가 요구사항에는 안맞지만 괜찮네