Transform[] childList = GameObject.GetComponentsInChildren<Transform>();

if (childList != null) {
	for (int i = 1; i < childList.Length; i++) {
		if (childList[i] != transform)
			Destroy(childList[i].gameObject);
	}
}

 

GameObject = 부모 오브젝트

for문에서 i=0 부터 시작하면 부모 오브젝트도 같이 삭제됨

 

 

'Study > Unity' 카테고리의 다른 글

[Unity] 안드로이드에서 뒤로가기  (0) 2021.07.26
[Unity] 게임 일시정지 기능  (0) 2021.07.26

 

앞선 <nodemailer를 통해 이메일 인증 구현> 게시글에서 언급했지만 노드 리액트 기초 강의 이후부터는 John Ahn님이 제공해주시는 보일러 플레이트 틀 바탕으로 코드를 수정한 뒤 진행된다. 

server>routes폴더 안에 파일들을 보일러 플레이트에서 가져오고 index.js 파일과 프론트에서 백으로 api 요청하는 부분을 알맞게 수정했다.

그 외 나머지는 대부분 노드 리액트 기초 강의 그대로 사용했다. (but 부분적으로 코드 수정 필요)

↓참고 사이트

https://github.com/jaewonhimnae/react-shop-app

 

 

게시글 작성 · 정렬 · 검색 · 상세보기 등과 같이 게시글과 관련된 기초적인 부분은 John Ahn님의 Online Shopping Mall Clone 강의를 참고하여 진행했다. 

↓참고 사이트

https://www.youtube.com/playlist?list=PL9a7QRYt5fqlWKC_wejtfUHYb9uyS8Cxw 

 

Online Shop Clone

 

www.youtube.com

 

 

UploadPage

 

게시글 작성 페이지

 

가장 먼저 할 일은 위 이미지와 같은 게시글 작성 페이지를 만드는 것이다.

client>src>components>views>UploadTemplatePage 폴더를 만들고 그 안에 UploadTemplatePage.js 파일을 생성한다.

그리고나서 rfce를 입력해서 functional component를 생성한다.

(내가 진행한 프로젝트가 태블릿 템플릿 공유 웹사이트이기 때문에 강의에서 Product라고 나오는 부분을 모두 Template으로 치환해서 작성했다. )

 

 

게시글 작성 페이지에 접근할 수 있게 App.js에 Route path를 작성한다. 

// App.js


import UploadTemplatePage from './views/UploadTemplatePage/UploadTemplatePage'


function App() {
	return (
    	<Suspense fallback={(<div>Loading...</div>)}>
        	<NavBar />
            <div style={{ paddingTop: '75px', minHeight: 'calc(100vh - 80px)' }}>
            	<Switch>
                	...
                	<Route exact path="/template/upload" component={ Auth(UploadTemplatePage, true) } />
                	...
                </Switch>
            </div>
            <Footer />
        </Suspense>
    );
}

export default App;

 

 

그런 다음 상단 바(NavBar)에 게시글 작성 페이지로 이동할 수 있는 메뉴를 만든다. 

참고로 VSC에서 빠르게 해당 파일로 이동하려면 Ctrl + P를 누르고 파일 이름을 입력하면 된다.

// client>src>components>views>NavBar>Sections>RightMenu.js

...

return (
    <Menu mode={props.mode}>
    	<Menu.Item key="upload">
        	<a href="/product/upload">Upload</a>
        </Menu.Item>
		<Menu.Item key="logout">
        	<a onClick={logoutHandler}>Logout</a>
        </Menu.Item>
    </Menu>
)

...

 

 

이제 아까 생성했던 UploadTemplatePage.js에 코드를 작성해야 하는데, John Ahn님의 강의에서 알려주는 코드를 바탕으로 현재 진행중인 프로젝트에 맞게 코드를 수정 및 추가했다. 

디자인적인 부분은 노드 리액트 기초 강의에서 언급되었던 ant design을 사용하여 적용했다. 

// UploadTemplatePage.js

import React, { useState } from 'react';
import { Typography, Button, Form, Input, message } from 'antd';
import axios from 'axios';
import { 
    Categories, Detail_1, Detail_2, Detail_3, Detail_4, Detail_5, Detail_6 
} from './Sections/Datas';


const { Title } = Typography;
const { TextArea } = Input;

var Detail = Detail_1;

function UploadTemplatePage() {
    const [TitleValue, setTitleValue] = useState("")
    const [DescriptionValue, setDescriptionValue] = useState("")
    const [CategoryValue, setCategoryValue] = useState(1)
    const [DetailValue, setDetailValue] = useState(1)
    
    const onTitleChange = (event) => {
        setTitleValue(event.currentTarget.value)
    }
    const onDescriptionChange = (event) => {
        setDescriptionValue(event.currentTarget.value)
    }
    
    const onCategorySelectChange = (event) => {
        setCategoryValue(event.currentTarget.value)
        switch (event.currentTarget.value) {
            case '1': 
                Detail = Detail_1.slice();
                setDetailValue(1);
                break;
            case '2': 
                Detail = Detail_2.slice();
                setDetailValue(1);
                break;
            case '3': 
                Detail = Detail_3.slice();
                setDetailValue(1);
                break;
            case '4': 
                Detail = Detail_4.slice();
                setDetailValue(1);
                break; 
            case '5': 
                Detail = Detail_5.slice();
                setDetailValue(1);
                break; 
            case '6': 
                Detail = Detail_6.slice();
                setDetailValue(1);
                break; 
            default:
                break;
        }
    }
    const onDetailSelectChange = (event) => {
        setDetailValue(event.currentTarget.value)
    }

   
    return (
        <div style={{ maxWidth: '700px', margin: '2rem auto' }}> 
            <div style={{ textAlign:'center', marginBottom:'2rem' }}>
                <Title level={2}>Upload Template</Title>
            </div>
            <Form onSubmit >
                {/*DropZone*/}
                
                <br/>
                <br/>
                
                <label>Title</label>
                <Input
                    onChange={onTitleChange}
                    value={TitleValue}
                    style={{marginTop:'8px'}}
                />
                <br/>
                <br/>
                
                <label>Description</label>
                <TextArea
                    onChange={onDescriptionChange}
                    value={DescriptionValue}
                    style={{marginTop:'8px'}}
                />
                
                <br/>
                <br/>
                
                <label>Category</label>
                <select onChange={onCategorySelectChange}>
                	{Categories.map(item => (
                		<option key={item.key} value={item.key}>{item.value}</option>
                  	))}
                </select>
                
                <label>Detail</label>
                <select onChange={onDetailSelectChange}>
                	{Detail.map(item => (
                    	<option key={item.key} value={item.key}>{item.value}</option>
                   	))}
                </select>
                <br/>
                <br/>
              
                <Button>
                    Submit
                </Button>
            </Form>
        </div>
    )
}

export default UploadTemplatePage

 

 

그리고 UploadTemplatePage 폴더 안에 Sections 폴더를 하나 만들고 Datas.js 파일을 생성한다. 

이 파일에서는 카테고리와 세부 카테고리 정보를 관리한다. 

// client>src>components>views>UploadTemplatePage>Sections>Datas.js

const Categories = [
    { key:1, value: "다이어리" },
    { key:2, value: "플래너" },
    { key:3, value: "노트" },
    { key:4, value: "라이프" },
    { key:5, value: "스티커" },
    { key:6, value: "기타" },
]
const Detail_1 = [
    { key:1, value: "날짜형" },
    { key:2, value: "만년형" },
    { key:3, value: "일기장" },
]
const Detail_2 = [
    { key:1, value: "먼슬리" },
    ...
]
const Detail_3 = [
    { key:1, value: "줄" },
    ...
]
const Detail_4 = [
    { key:1, value: "가계부" },
    ...
]
const Detail_5 = [
    { key:1, value: "메모지" },
    ...
]
const Detail_6 = [
    { key:1, value: "트래커" },
    ...
]


export {
    Categories, 
    Detail_1,
    Detail_2,
    Detail_3,
    Detail_4,
    Detail_5,
    Detail_6
}

 

 

코드를 저장하고 터미널 창에 npm run dev를 입력해서 실행시키면 다음과 같이 간단하게 제목, 설명을 입력하고 카테고리와 세부 카테고리를 선택할 수 있는 게시글 작성 페이지가 뜨는 것을 확인할 수 있다. 

UploadTemplatePage

 

 

FileUpload

파일 업로드 기능 구현을 위해 먼저 백엔드 부분을 작성해보겠다. 

server폴더 안에 있는 index.js에 다음 코드를 추가하고 routes폴더에 template.js 파일을 생성한다.

// server > index.js 

app.use('/api/template', require('./routes/template'));

 

 

클라이언트에서 이미지를 업로드하면 백엔드에서 이 이미지를 받아 노드 서버에 저장해야 하는데 이때 Multer 라이브러리가 필요하기 때문에 npm install multer --save를 통해 다운받는다. 

 

그런 다음, template.js에 다음 코드를 추가하고 이미지 파일을 저장할 uploads라는 폴더를 root directory에 생성한다. 

// template.js

const express = require('express');
const router = express.Router();
const multer = require('multer');
const { auth } = require("../middleware/auth");


var storage = multer.diskStorage({
    destination: (req, file, cb) => {
        cb(null, 'uploads/')
    }, //파일 저장 위치
    filename: (req, file, cb) => {
        cb(null, `${Date.now()}_${file.originalname}`)
    }, //파일 이름 ex)1126201_hello.png
    fileFilter: (req, file, cb) => {
        const ext = path.extname(file.originalname)
        if (ext !== '.jpg' || ext !== '.png') { //jpg와 png 파일만 허용, 다른 파일 확장자 추가 가능
            return cb(res.status(400).end('only jpg, png are allowed'), false)
        }
        cb(null, true)
    }
})

var upload = multer({ storage: storage }).single("file")


router.post("/uploadImage", auth, (req, res) => {
    upload(req, res, err => {
        if(err) return res.json({ success: false, err })
        return res.json({ success: true, image: res.req.file.path, fileName: res.req.file.filename })
    	//저장에 성공하면 이미지 경로와 파일 이름이 클라이언트로 전달됨
    })
});

module.exports = router;

 

 

 

이제 클라이언트에서 파일 업로드 페이지를 만들기에 앞서 client>src>components>utils 폴더를 하나 생성한다. 

이 폴더에는 자주 사용되는 기능들을 구현한 스크립트 파일들을 모아두어 다른 js파일에서 쉽게 import하여 사용할 수 있게 한다. 

FileUpload.js도 다른 곳에서 사용할 수 있기 때문에 utils 폴더에 생성하여 코드를 작성할 것이다. 

 

 

FileUpload.js를 생성하고나면 rfce를 입력하여 functional component를 생성한다. 

그리고나서 터미널 창에 cd client 입력후 npm install react-dropzone --save를 통해 react-dropzone을 설치한다. 

이제 FileUpload.js에 다음 코드를 작성한다. 

// FileUpload.js


import React, { useState } from 'react';
import DropZone from 'react-dropzone';
import { PlusOutlined } from '@ant-design/icons';
import axios from 'axios';

function FileUpload(props) {
    const [Images, setImages] = useState([]) //배열을 사용하는 이유는 여러 이미지를 저장할 것이기 때문

    const onDrop=(files) => {
        let formData = new FormData();
        const config = {
            header: {'content-type': 'multipart/form-data'}
        }
        formData.append("file", files[0])
        
        //선택한 이미지를 노드 서버에 저장 
        axios.post('/api/template/uploadImage', formData, config)
        .then(response => {
            if(response.data.success) {
               setImages([...Images, response.data.image]) // ...Images는 기존 이미지 배열, response.data.image는 새 이미지
               props.refreshFunction([...Images, response.data.image]) //UploadTemplatePage.js에 있는 updateImages() 실행시킴
            } else {
                alert('이미지 저장에 실패했습니다.')
            }
        })
        
    }

    const onDelete = (image) => {
        const currentIndex = Images.indexOf(image) //선택한 이미지의 index

        let newImages = [...Images] //현재 이미지 배열
        newImages.splice(currentIndex, 1) //splice를 통해 선택한 이미지를 배열에서 잘라내기

        setImages(newImages)
        props.refreshFunction(newImages)
    }


    return (
        <div style={{ display: 'flex', justify-content: 'space-between' }}>
            <DropZone
                onDrop={onDrop}
                multiple={false} //이미지를 한 번에 하나씩 등록
                maxSize={80000000}
            >
                {({getRootProps, getInputProps}) => (
                    <div style={{ width:'300px', height:'240px', border:'1px solid lightgray', 
                    display:'flex', alignItems:'center', justifyContent:'center' }}
                        {...getRootProps()}
                    >
                        <input {...getInputProps()}/>
                        <PlusOutlined style={{ fontSize:'3rem' }} />
                    </div>
                )}
            </DropZone>
            
            <div style={{ display:'flex', width:'350px', height:'240px', overflowX:'scroll' }}>
            	{Images.map((image, index) => (
                    <div onClick={()=>onDelete(image)} key={index}>
                    	<img style={{ minWidth:'300px', width:'300px', height:'240px' }}
                            src={`http://localhost:5000/${image}`} alt={`templateImg-${index}`} />
                    </div>
                ))} 
            </div>
        </div>
    )
}

export default FileUpload

 

splice 함수에 대한 정리는 아래 '[Javascript] splice' 함수 글에서 확인할 수 있다. 

 

[Javascript] splice 함수

초기화 let arr = ['a', 'b', 'c', 'd'] 삽입 arr.splice(0, 0, 'f')​ arr.splice(5, 0, 'e') → 5 이상의 수를 넣어도 결과는 같음 제거 arr.splice(0, 2)​

sungeun97.tistory.com

 

 

UpdateTemplatePage.js에는 다음 코드를 추가한다. 

// UploadTemplatePage.js

import React, { useState } from 'react';
import axios from 'axios';
...
import FileUpload from '../../utils/FileUpload';

...

function UploadTemplatePage() {
    const [Images, setImages] = useState([])
    
    ...
    
    
    const updateImages = (newImages) =>{
        setImages(newImages)
    }
    

    return (
        <div style={{ maxWidth: '700px', margin: '2rem auto' }}> 
            <div style={{ textAlign:'center', marginBottom:'2rem' }}>
                <h2>Upload Template</h2>
            </div>
            <Form onSubmit >
                {/*DropZone*/}
                <FileUpload refreshFunction={updateImages}/>
                <br/>
                <br/>
                
                ...
                
                <div style={{textAlign:'center', marginTop:'2%', marginBottom:'30px'}}>
                <Button>
                    Submit
                </Button>
                </div>
            </Form>
        </div>
    )
}

export default UploadTemplatePage

 

refreshFunction

fileUpload.js의

props.refreshFunction([...Images, response.data.image])

을 예로 설명하자면, 기존 이미지들의 배열인 ...Images에 새로 업로드한 이미지인 response.data.image를 추가한 배열을

 

refreshFunction을 통해 UploadTemplatePage.js에 있는 updateImages()에 인수로 넘겨주고,

<FileUpload refreshFunction={updateImages}/>

 

이 updateImages()는 넘겨 받은 배열을 UpdateTemplatePage.js에 있는 Images배열에 저장한다. 

const updateImages = (newImages) =>{
    setImages(newImages)
}

 

참고로 refreshFunction은 다른 이름으로 바꿔서 사용해도 무관한다. 

 

 

 

MongoDB에 저장하기

작성한 게시글을 MongoDB에 저장하기 위해서 먼저 백엔드에 User 모델처럼 Template 모델을 만들어야 한다. 

Template 모델은 server폴더에 있는 models 폴더 안에 Template.js파일을 하나 생성해서 만든다. 

// server > models > Template.js

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const templateSchema = mongoose.Schema({
    writer: {
        type: Schema.Types.ObjectId,
        ref: 'User' //User model에서 user에 대한 모든 정보를 가져올 수 있음
    },
    title: {
        type: String, 
        maxlength: 50
    },
    description: {
        type: String,
    },
    category: {
        type: Number,
        default: 1
    },
    detail: {
        type: Number,
        default: 1
    },
    images: {
        type: Array,
        default: []
    },
    downloads: {
        type: Number,
        maxlength: 1000,
        default: 0
    },
    views: {
        type: Number,
        default: 0
    }
}, {timestamps: true}) //timestamps를 통해서 생성 및 갱신 시간을 알 수 있음


const Template = mongoose.model('Template', templateSchema)

module.exports = { Template }

 

 

그리고나서 server>routes>template.js에 다음 코드를 추가한다. 

// server > routes > template.js

const { Template } = require("../models/Template");

...

router.post("/uploadTemplate", auth, (req, res) => {
    //클라이언트에서 받은 모든 정보를 DB에 저장
    const template = new Template(req.body)
    template.save((err) => {
        if(err) return res.status(400).json({ success: false, err })
        return res.status(200).json({ success: true })
    })
});

...

 

 

클라이언트 부분으로 넘어와 UploadTemplatePage.js에 onSubmit 함수를 작성한다. 

// UploadTemplatePage.js


...

function UploadTemplatePage(props) {
    
    ...
    
    
    const onSubmit = (event) => {
        event.preventDefault()

        if(!TitleValue || !DescriptionValue || Images.length == 0) { // 입력하지 않은 영역이 있는 경우
            return alert('Fill all the fields first!')
        }

        const variables = {
            //Redux를 통해 가져온 로그인된 유저의 정보를 props.user.userData로 접근하여 사용할 수 있다.
            writer: props.user.userData._id,  
            title: TitleValue,
            description: DescriptionValue,
            images: Images,
            category: CategoryValue,
            detail: DetailValue,
        }

        axios.post('/api/template/uploadTemplate', variables)
            .then(response => {
                if(response.data.success) {
                    alert('Template Successfully Uploaded')
                    props.history.push('/')
                } else {
                    alert('Failed to upload Template')
                }
            })
    }

    return (
        <div style={{ maxWidth: '700px', margin: '2rem auto' }}> 
            
            ...
            
            <Form onSubmit={onSubmit} >
                
                ...
                
                <div style={{textAlign:'center', marginTop:'2%', marginBottom:'30px'}}>
                <Button
                    onClick={onSubmit}
                >
                    Submit
                </Button>
                </div>
            </Form>
        </div>
    )
}

export default UploadTemplatePage

 

 

 

이제 실행시켜서 게시글 작성을 완성한 뒤 Submit 버튼을 누르면 다음과 같이 'Template Successfully Uploaded' 알림창이 뜨고, 확인 버튼을 누르면 Landing Page로 이동한다. 

게시글 작성 완료

 

 

MongoDB로 넘어와 templates를 확인해보니 성공적으로 게시글 정보가 저장된 것을 확인할 수 있었다. 

MongoDB - templates

 

 

 

(이 강의부터 John Ahn님이 제공하시는 보일러 플레이트 틀 바탕으로 코드를 수정한 뒤 진행됨)

요즘 회원가입 시에는 휴대번호나 이메일 인증은 필수이기 때문에 노드 리액트 기초 강의를 끝내고 먼저 한 일은 이메일 인증이었다. 

 

먼저, 이메일 인증과 관련해서 찾아보던 중 nodemailer라는 것을 알게 되었고, 이 모듈을 사용하기로 했다. 

Nodemailer는 쉽게 이메일을 보낼 수 있도록 하는 Node.js 애플리케이션용 모듈이다. 

 

먼저 터미널 창에 npm install nodemailer dotenv --save를 하여 다운받는다. 

이메일을 보내는 사람의 이메일 주소와 비밀번호가 코드에 포함되는데, dotenv를 사용하여 이를 따로 관리할 수 있다. 

 

server 폴더 안에 mail.js 파일을 생성하고 다음 코드를 작성한다. 

//mail.js


require('dotenv').config();

const mailer = require("nodemailer");

const { Hello } = require("./hello_template");


const getEmailData = (to, authCode) => {

    data = {
    
        from: "BabyAngel",
        
        to,
        
        subject: "Hello",
        
        html: Hello(authCode)
        
    }
    
    return data;
    
}


const sendEmail = (to, authCode) => {

    const smtpTransport = mailer.createTransport({
    
        service: "Gmail",
        
        auth: {
        
            user: process.env.NODEMAILER_USER,//보내는 사람 이메일
            
            pass: process.env.NODEMAILER_PASS //비밀번호
            
        }
        
    })


    const mail = getEmailData(to, authCode)
    

    smtpTransport.sendMail(mail, function(error, response) {
    
        if(error) {
        
            console.log(error)
            
        } else {
        
            console.log("email sent successfully")
            
        }
        
        smtpTransport.close();
        
    })

}

module.exports = { sendEmail }

 

그리고 마찬가지로 server 폴더 안에 hello_template.js 파일을 생성하여 사용자에게 보낼 이메일 형식을 html 형식으로 작성한다. 

const Hello = data => {

    return `
        <!DOCTYPE html>
        <html style="margin: 0; padding: 0;">
            <head>
                <title>이메일 인증하기</title>
            </head>
            <body style="margin: 0; padding: 0; font-size:15px;">
                <div>인증번호는 ${data} 입니다.</div>
            </body>
        </html>
    `
}

module.exports = { Hello }

 

 

mail.js에서 process.env.NODEMAILER_USER와 process.env.NODEMAILER_PASS를 따로 관리해주는 .env 파일을 root directory에 생성한다. 만약 코드를 git에 올린다면 .env파일은 개인정보를 담고 있기 때문에 .gitignore에 추가해서 git에 올라가지 않게 해줘야 한다. 

 

// .env

NODEMAILER_USER = "이메일을 보내는 사람의 이메일 주소"
NODEMAILER_PASS = "이메일을 보내는 사람의 비밀번호"

각 NODEMAILER_USER와 NODEMAILER_PASS에는 실제 본인 이메일과 비밀번호를 입력하면 된다. 

 

 

그런 다음 server>routes>users.js에 인증코드를 발송하는 sendEmail api를 작성한다. 

//users.js

const { sendEmail } = require('../mail');


router.post('/sendEmail', (req, res) => {
    sendEmail(req.body.email, req.body.auth)
    return res.status(200).json({
      success: true
    })
})

 

 

이제 백엔드 부분은 완성 되었고 프론트에서 인증버튼을 눌렀을 때 백엔드로 요청을 보내는 코드를 작성하면 되겠다. 

(이름, 비밀번호, 비밀번호 인증 부분의 코드는 앞에서 다뤘기 때문에 다음 코드에서 생략되어 있다.)

import React, { useState } from 'react' 
import { useDispatch } from 'react-redux';
import { registerUser } from '../../../_actions/user_action';
import { withRouter } from 'react-router-dom';
import axios from 'axios';

var state = {
    createdAuthCode: "",
    authCodeCheck: false
}

function RegisterPage(props) {
    const dispatch = useDispatch()
    const [Email, setEmail] = useState("")
    const [AuthCode, setAuthCode] = useState("")

    const onEmailHandler = (event) => {
        setEmail(event.currentTarget.value)
    }
    const onAuthCodeHandler = (event) => {
        setAuthCode(event.currentTarget.value)
    }
    const onNameHandler = (event) => {
        setName(event.currentTarget.value)
    }
   

    const onSendMailHandler = (event) => {
        event.preventDefault();
        
        state.createdAuthCode = Math.random().toString(36).substr(2,6); //랜덤 문자열 6자리 생성

        const dataToSubmit = {
            email: Email,
            auth: state.createdAuthCode
        }
        console.log('authCode = '+state.createdAuthCode)
        axios.post("/api/users/sendEmail", dataToSubmit)
        .then(response => {
            alert("인증코드가 발송되었습니다.")
        })
    }
    const onCheckHandler = (event) => {
        event.preventDefault(); //page refresh 막아줌

        console.log(state.createdAuthCode +" == "+AuthCode)

        if(state.createdAuthCode == AuthCode) {
            state.authCodeCheck = true;
            alert("이메일 인증에 성공하셨습니다")
        }
        else {
            state.authCodeCheck = false;
            alert("인증 코드가 일치하지 않습니다.")
        }
    }
   
    return (
        <div style ={{ display: 'flex', justifyContent: 'center', alignItems: 'center',
        width: '100%', height: '100vh' }}>
            <div >
            <form style={{ display: 'flex', flexDirection: 'column' }}
                onSubmit={onSendMailHandler}>
                <h2>RegisterPage</h2>
                <br/>
                <label>Email</label>
                <div>
                    <input type="email" value={Email} onChange={onEmailHandler} required/>
                    <button type="submit">
                        send code
                    </button>
                </div>
            </form>
            <form style={{ display: 'flex', flexDirection: 'column' }}
                onSubmit={onCheckHandler}
            >
                <label>Authentication Code</label>
                <div>
                    <input type="text" value={AuthCode} onChange={onAuthCodeHandler} required/>
                    <button type="submit">
                        check
                    </button>
                </div>
            </form>
            </div>
        </div>
    )
}

export default withRouter(RegisterPage)

 

여기서 인증코드를 생성하는 부분은 Math.random().toString(36).substr(2,6) 이다. 

Math.random().toString(36)을 하게 되면, 

Math.random()은 0 이상 1 미만의 구간에서 근사적으로 균일한 부동소숫점 의사난수를 반환하고, toString()에 36을 넣어서 이 수를 36진수로 표현하게 된다. 

그 결과 0.6feh4o7j... 처럼 나오게 되는데 소숫점 아래 6자리만 사용할 것이기 때문에 substr(2, 6)을 사용한 것이다.

 

 

이제 실행시키고 이메일을 입력해서 send code 버튼을 누르면 먼저 VSC 터미널 창에서 다음과 같이 로그가 찍혀 성공적으로 이메일이 전송되었음을 알 수 있다. 

백엔드 로그

 

 

인증코드 이메일

현재 이 글은 프로젝트가 모두 끝나고 작성하는 것이고, 아래 인증코드에 대한 이메일은 삭제했었는지 남아있지 않았다.. 하지만 nodemailer를 통해 성공적으로 이메일이 전송되어 온 것은 확인할 수 있다. 

 

 

이메일 인증 성공

인증코드를 입력하고 check 버튼을 누르면 이메일 인증에 성공했음을 알려주는 alert창이 뜬다. 

 

 

+ Recent posts