์ผ๋์ผ ๊ด๊ณ๋ A๊ฐ B์ ์ธ์คํด์ค๋ฅผ ํ๋๋ง ํฌํจํ๊ณ B๊ฐ A์ ์ธ์คํด์ค๋ฅผ ํ๋๋ง ํฌํจํ๋ ๊ด๊ณ์ด๋ค. ์๋ฅผ ๋ค์ด ์ฌ์ฉ์ ๋ฐ ํ๋กํ ์ํฐํฐ๋ฅผ ๋ณด๋ฉด, ์ฌ์ฉ์๋ ํ๋์ ํ๋กํ๋ง ๊ฐ์ง ์ ์์ผ๋ฉฐ, ํ๋กํ์ ํ๋์ ์ฌ์ฉ์๋ง ๊ฐ์ง ์ ์๋ค.
@JoinColumn()์ ์ค์ ํ ์ชฝ์ ํ
์ด๋ธ์๋ ํด๋น๋๋ ์ํฐํฐ ํ
์ด๋ธ์ ๋ํ relation id์ foreign keys๋ฅผ ํฌํจํ๋ค.
@JoinColumn์ ๊ด๊ณ์ ํ ์ชฝ, ์ฆ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ
์ด๋ธ์ foreign key๊ฐ ์์ด์ผ ํ๋ ์ชฝ์๋ง ์ค์ ํด์ผ ํฉ๋๋ค.
@InputType()
@ObjectType()
@Entity()
export class Verification extends CoreEntity {
@Column()
@Field((type) => String)
code: string;
@OneToOne((type) => User)
@JoinColumn()
user: User;
}
Case) Verification์ ํตํด ๊ทธ ์์ User์ ์ ๊ทผํด์ User์ email Verified๋ฅผ false์์ true๋ก ๋ฐ๊ฟ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ Verification์ชฝ์ @JoinColumn()์ ์ถ๊ฐํ๊ณ user๋ฅผ ํตํด ์์ฑํ foreign key์ธ userId์ ์ถ๊ฐํ๋๋ก ํ ๊ฒ์ด๋ค.
TypeORM์์๋ ๊ด๊ณ๋ฅผ ๊ฐ์ง๊ณ ์๋ ํ๋๋ TypeORM์ ๋ฐ๋ก ์ง์ ํ์ง ์์ผ๋ฉด ์๋์ผ๋ก ํด๋น ํ๋๋ฅผ ๋ณด์ฌ์ฃผ์ง ์๋๋ค.
true๋ก ์ค์ ์ relation id ๊ฐ๋ง์ ๊ฐ์ ธ์จ๋ค. (userId: 10)
์ํฐํฐ์ ๋ชจ๋ ๊ด๊ณ ID๋ฅผ ๋ก๋ํ๊ณ ๊ด๊ณ ๊ฐ์ฒด๊ฐ ์๋ ๊ด๊ณ ๊ฐ์ ๋งคํํ๋ค.
relations๋ฅผ ํตํด ํด๋น ํ๋์ ์ ์ฒด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์๋ ์๋ค.
async verifyEmail(code: string): Promise<boolean> {
const verification = await this.verification.findOne({
where: { code },
relations: ['user'],
});
if (verification) {
verification.user.verified = true;
this.users.save(verification.user);
}
return false;
}
verified ์
๋ฐ์ดํธ๋ฅผ ์ํด this.user.Save(verification.user) ํ ๋ BeforeUpdate๊ฐ ํญ์ ์คํ๋์ด password๊ฐ ์ฌ๋ฌ๋ฒ ํด์ฌ๋๋ค.
{select: false}Entity ํ์ผ์์ ํด๋น Column(password)์ {select: false}๋ฅผ ์ฝ์
ํด find ์คํ์(find๋ฉ์๋๋ค)๋ฅผ ํตํด ํด๋น ์ํฐํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋ ํด๋น column์ ์ ํ๋์ด์ง์ง ์๋๋ก ํฉ๋๋ค.
find๋ฉ์๋๋ฅผ ํตํด ๋ฐ์ ๊ฐ์ฒด์ ํด๋น column์ด ์์ ๊ฒฝ์ฐ์๋ง save๊ฐ ์คํ๋๋๋ก if๋ฌธ์ ์ฌ์ฉํ๋ค.
โป Column์ {select: false}๋ก ์ง์ ํ์ ๊ฒฝ์ฐ ์ด์ ์ ์ง๋์ ์ฝ๋๋ค ์ค find๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ๋ถ๋ถ์์ ํด๋น column์ ๋ฆฌํด๋ฐ์ง ๋ชปํด ์๋ฌ๊ฐ ๋ ์ ์๋ค. ์ด๋ฐ ๊ฒฝ์ฐ find๋ฉ์๋์ select:['column']์ ๋ฃ์ด ํด๋น Column์ด ๋ฆฌํด๋๋๋ก ์ค์ ํด์ฃผ์ด์ผ ํ๋ค.
this.user.update(verification.user)await this.verification.update(
verification.user.id,
{ verified: true });
๊ฐ๋ฐ์๋ฅผ ์ํ ํธ๋์ ์
์ด๋ฉ์ผ API ์๋น์ค
https://www.mailgun.com
Nodemailer ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ Nest.js ํ๋ ์์ํฌ(node.js)์ฉ ๋ฉ์ผ๋ฌ ๋ชจ๋
pug์ ์ฐ๋ํ ์ ์์ผ๋ฉฐ ์ง์ html์ ์์ฑํด ๋ณต์กํ๊ณ ํ๋ คํ ์ด๋ฉ์ผ ์ ์ก์ด ๊ฐ๋ฅํ๋ค.
mailgun ๋ํ mailgun templete์ ์ด์ฉํด ๋ณต์กํ ์ด๋ฉ์ผ ์ ์ก์ด ๊ฐ๋ฅํ๋ค.
https://nest-modules.github.io/mailer
https://github.com/nest-modules/mailer
npm i got
Node.js๋ฅผ ์ํ ์ธ๊ฐ ์นํ์ ์ด๊ณ ๊ฐ๋ ฅํ HTTP request ๋ผ์ด๋ธ๋ฌ๋ฆฌ
(ํ๋ก ํธ์๋์์์ Fetch์ ๊ฐ์ ์ญํ ์ ํ๋ค.)
npm i form-data
์ฝ์ ์ ์๋ "multipart/form-data" ์คํธ๋ฆผ์ ์์ฑํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
๋ค๋ฅธ ์น ์ ํ๋ฆฌ์ผ์ด์
์ form์ submitํ๊ณ , ํ์ผ์ ์
๋ก๋ํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ค.
์ฌ๊ธฐ์๋ mailgun์ api๋ก post๋ฅผ ๋ณด๋ผ ๋ form์ด ์ฌ์ฉ๋์๋ค.
Form์ ํ์์ Mailgun Api์ ๋ณด์ ๋์์๋ ํ์์ ๋ฐ๋ฅธ๋ค.
private async sendMail(subject: string, context: string) {
const form = new FormData();
form.append('from', `Excited User <mailgun@${this.options.domain}>`);
form.append('to', `tem123@nate.com`);
form.append('subject', subject);
form.append('text', context);
const response = await got(
`https://api.mailgun.net/v3/${this.options.domain}/messages`,
{
method: 'POST',
headers: {
Authorization: `Basic ${Buffer.from(
`api:${this.options.apiKeys}`,
).toString('base64')}`, // Buffer: Binary์ ๋ฐ์ดํฐ๋ฅผ ๋ด์ ์ ์๋ ๊ฐ์ฒด
},
body: form,
},
);
console.log(response.body);
}
ํธ๋ค๋ฐ๋ ๊ฐ๋จํ ํ ํ๋ฆฟ ์ธ์ด์ด๋ค. ํ ํ๋ฆฟ๊ณผ input๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ HTML ๋๋ ๊ธฐํ ํ ์คํธ ํ์์ ์์ฑํ๋ค.
ํ
ํ๋ฆฟ API๋ฅผ ํตํด ์ ์ฅ๋ ํ
ํ๋ฆฟ ์ด๋ฆ
ex) formData.append('template', 'nuber-eats');
v: prefix๋ฅผ ํตํด ์ปค์คํ
JSON ๋ฐ์ดํฐ๋ฅผ ๋ฉ์ธ์ง์ ์ด๋ฆ์ ๋ถ์ฌ ๋ณด๋ผ ์ ์๋ค.
ex) formData.append('v:code', 'abcd1234')
form.append('template', 'verify-email');
form.append('v:username', 'yeop');
form.append('v:code', '15646');