1 – EmailJS 계정과 이메일 서비스 준비
1.1 – EmailJS 계정 만들기
- 브라우저 주소창에
https://dashboard.emailjs.com/sign-up을 입력하고 Enter를 누릅니다. - 가입 화면이 열리면 이메일 주소와 비밀번호를 입력합니다.
- 구글 계정으로 가입하려면 [Sign up with Google]을 클릭합니다.
- [Sign Up] 버튼을 클릭합니다.
- 가입한 이메일로 인증 메일이 오면 메일 안의 확인 링크를 클릭합니다.
- 로그인하면 대시보드 화면이 열립니다.
EmailJS는 브라우저에서 실행되는 자바스크립트만으로 이메일을 보내는 서비스입니다.
이메일을 보내려면 원래는 메일을 대신 발송해 줄 서버가 필요합니다.
서버는 브라우저가 보낸 요청을 받아 실제 메일 서버에 전달하는 중간 프로그램입니다.
EmailJS는 이 서버 역할을 대신 맡습니다.
브라우저에서 바로 EmailJS로 요청을 보내면, EmailJS가 연결된 메일 계정으로 메일을 발송합니다.
그래서 백엔드(브라우저가 아니라 서버에서 도는 프로그램) 코드를 직접 만들지 않아도 문의 폼이 동작합니다.
편지를 부칠 때 우체국에 맡기면 우체국이 대신 배달해 주는 것과 같습니다.
대시보드는 EmailJS의 설정을 모아 둔 관리 화면입니다.
왼쪽에 [Email Services], [Email Templates], [Account] 같은 메뉴가 세로로 있습니다.
앞으로 이 세 메뉴에서 서비스 ID, 템플릿 ID, 공개 키 세 값을 하나씩 받습니다.
값을 받을 때마다 메모장에 적어 두면 나중에 코드를 쓸 때 화면을 오가지 않아도 됩니다.
이 실습에서 문의를 받을 이메일은 본인 이메일을 씁니다.
문의 폼으로 들어온 메일이 이 주소로 도착하는지 직접 확인해야 하기 때문입니다.
EmailJS 무료 플랜은 한 달에 200번까지 발송할 수 있고, 이메일 템플릿은 2개까지 만들 수 있으며, 한 번에 보내는 요청 크기는 50Kb까지입니다. 이 실습에는 무료 플랜으로 충분합니다. (2026-09-02 확인, 출처 www.emailjs.com/pricing)
1.2 – 이메일 서비스 연결하기
- 왼쪽 메뉴에서 [Email Services]를 클릭합니다.
- [Add New Service] 버튼을 클릭합니다.
- 서비스 목록에서 [Gmail]을 클릭합니다.
- [Connect Account] 버튼을 클릭합니다.
- 구글 로그인 창이 뜨면 메일을 받을 본인 구글 계정을 선택하고 권한을 허용합니다.
- [Create Service] 버튼을 클릭합니다.
- 연결된 서비스 화면에 [Service ID]가 표시됩니다.
- [Service ID] 값을 메모장에 적어 둡니다.
이메일 서비스는 EmailJS가 실제로 메일을 보낼 때 거치는 메일 계정입니다.
Gmail이나 Outlook 같은 계정을 연결합니다.
Service ID는 연결한 여러 메일 계정 가운데 어느 계정으로 보낼지 가리키는 값입니다.
service_ab12cde처럼 service_로 시작하는 문자열입니다.
코드에서 이 값을 넘기면 EmailJS는 그 계정으로 메일을 발송합니다.
서비스를 만들 때 [Send test email to verify configuration](설정 확인용 테스트 메일 보내기) 항목이 켜져 있으면, 연결이 끝난 직후 확인용 메일 한 통이 도착합니다. 이 메일이 오면 계정 연결이 제대로 된 것입니다.
2 – 이메일 템플릿과 공개 키 준비
2.1 – 이메일 템플릿 만들기
- 왼쪽 메뉴에서 [Email Templates]를 클릭합니다.
- [Create New Template] 버튼을 클릭합니다.
- 편집 화면이 열리면 Subject 칸에
새 문의: {{subject}}를 입력합니다. - 내용 편집기(Content)에 아래 문장을 입력합니다.
보낸 사람: {{user_name}} ({{user_email}})
{{message}}- 오른쪽 설정에서 [To Email](받는 사람) 칸에 메일을 받을 본인 이메일 주소를 입력합니다.
- [Reply To](답장 받을 주소) 칸에
{{user_email}}을 입력합니다. - [Save] 버튼을 클릭합니다.
- 저장한 템플릿의 [Template ID]를 메모장에 적어 둡니다.
템플릿은 보낼 메일의 제목·내용·수신자 형식을 미리 정해 둔 틀입니다.
폼에서 받은 값이 이 틀의 빈자리에 채워집니다.
이중 중괄호로 감싼 {{user_name}} 같은 표기는 변수입니다.
변수는 코드에서 넘긴 값으로 바뀌는 자리입니다.
폼에서 이름을 홍길동으로 보내면 {{user_name}} 자리에 홍길동이 들어간 메일이 발송됩니다.
[Reply To] 칸에 {{user_email}}을 넣으면, 도착한 문의 메일에서 [답장]을 눌렀을 때 문의한 사람 주소로 답장이 갑니다.
Template ID는 template_xy34fgh처럼 template_로 시작하는 문자열입니다.
코드에서 이 값을 넘기면 EmailJS는 그 템플릿 형식으로 메일을 만듭니다.
여기서 정한 변수 이름 네 개(user_name, user_email, subject, message)를 메모장에 함께 적어 둡니다. 뒤에서 만들 폼의 입력칸 name과 이 변수 이름이 정확히 같아야 값이 메일에 채워집니다. 하나라도 다르면 그 자리가 빈 채로 발송됩니다.
무료 플랜은 템플릿을 2개까지 만들 수 있습니다. 이 실습에는 템플릿 1개면 됩니다. (2026-09-02 확인, 출처 www.emailjs.com/pricing)
2.2 – 공개 키 확인하기
- 왼쪽 메뉴에서 [Account]를 클릭합니다.
- [General] 화면의 [API Keys] 영역에서 [Public Key]를 찾습니다.
- [Public Key] 값을 복사해 메모장에 적어 둡니다.

Public Key(공개 키)는 이 요청이 내 EmailJS 계정에서 나온 것임을 알리는 값입니다.
코드에서 이 값을 넘겨야 EmailJS가 요청을 내 계정의 것으로 받아들입니다.
AbCdEfGhIjKlMnOp처럼 글자와 숫자가 섞인 문자열입니다.
화면의 [Public Key] 칸에 보이는 값이 그것이며, 그 아래 [Private Key]는 코드에 넣지 않습니다.
이로써 코드에 넣을 값 세 가지(Service ID, Template ID, Public Key)를 메모장에 적어 두었습니다.
예전 EmailJS는 이 값을 User ID라고 불렀고, [Integration] 탭에 있었습니다. 현재는 이름이 Public Key로 바뀌었고 [Account]에서 확인합니다. 오래된 예제 코드에서 User ID라는 이름을 보면 지금의 Public Key와 같은 값으로 보면 됩니다. (2026-09-02 확인, 출처 www.emailjs.com/docs)
3 – HTML 파일에서 이메일 보내기
3.1 – HTML 폼 준비하기
- 코드 편집기(VS Code 등)에서 새 파일을 만듭니다.
- 아래 코드를 그대로 붙여 넣습니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>이메일 문의하기</title>
<style>
.email-form { max-width: 500px; margin: 20px auto; padding: 20px; border: 1px solid #ccc; border-radius: 8px; }
.form-group { margin-bottom: 15px; }
label { display: block; margin-bottom: 5px; font-weight: bold; }
input[type="text"], input[type="email"], textarea { width: 100%; padding: 8px; box-sizing: border-box; border: 1px solid #ddd; border-radius: 4px; }
button { background-color: #007bff; color: white; padding: 10px 15px; border: none; border-radius: 4px; cursor: pointer; }
button:hover { background-color: #0056b3; }
</style>
</head>
<body>
<div class="email-form">
<h2>이메일 보내기</h2>
<form id="contact-form">
<div class="form-group">
<label for="name">이름</label>
<input type="text" id="name" name="user_name" required>
</div>
<div class="form-group">
<label for="email">이메일 주소</label>
<input type="email" id="email" name="user_email" required>
</div>
<div class="form-group">
<label for="subject">제목</label>
<input type="text" id="subject" name="subject" required>
</div>
<div class="form-group">
<label for="message">내용</label>
<textarea id="message" name="message" rows="5" required></textarea>
</div>
<button type="submit">전송하기</button>
</form>
<p id="status"></p>
</div>
</body>
</html>이 코드는 서버로 데이터를 보내던 원래 폼을 EmailJS용으로 고친 것입니다.
<form>은 원래 [전송하기]를 누르면 action에 적힌 주소로 데이터를 보내고 페이지를 새로 고칩니다.
EmailJS는 이 기본 동작 대신 자바스크립트로 데이터를 EmailJS에 보냅니다.
그래서 원래 있던 action과 method를 지웠습니다.
폼에 id="contact-form"을 붙인 것은 자바스크립트가 이 폼을 찾을 수 있게 하기 위해서입니다.
각 입력칸의 name(user_name, user_email, subject, message)은 앞에서 템플릿에 정한 변수 이름과 같습니다.
버튼 아래에 넣은 <p id="status"></p>는 전송 결과 문구를 보여 줄 빈 자리입니다.
- 원하는 위치에
index.html이라는 이름으로 저장합니다. - 저장한
index.html을 더블클릭해 브라우저에서 엽니다.
문의 폼 화면이 나타나면 폼이 제대로 만들어진 것입니다.
아직 전송 기능을 붙이지 않았으므로 [전송하기]를 눌러도 메일은 가지 않습니다.
3.2 – SDK 불러오고 초기화하기
index.html의</head>바로 위에 아래 두 줄을 넣습니다.
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@emailjs/browser@4/dist/email.min.js"></script>
<script type="text/javascript">
emailjs.init({ publicKey: "여기에_공개_키" });
</script>여기에_공개_키자리를 메모장에 적어 둔 Public Key로 바꿉니다.- 파일을 저장합니다.
SDK는 EmailJS 기능을 브라우저에서 쓰도록 묶어 둔 자바스크립트 파일입니다.
첫 번째 줄은 CDN에서 EmailJS SDK를 받아옵니다.
CDN(Content Delivery Network)은 자바스크립트 파일을 인터넷에서 바로 받아 쓰도록 제공하는 서버입니다.
이 방식은 파일을 따로 내려받아 설치하지 않고 주소 한 줄로 SDK를 붙입니다.
두 번째 script의 emailjs.init은 공개 키를 EmailJS에 등록합니다.
이 등록을 해 두면 이후 전송 요청이 내 계정의 것으로 처리됩니다.
React나 Vue 같은 프레임워크에서 쓸 때는 CDN 대신 npm install @emailjs/browser 명령으로 설치하고 import emailjs from "@emailjs/browser"로 불러옵니다. 이 실습은 HTML 파일 하나로 하므로 CDN 방식을 씁니다. (2026-09-02 확인, 출처 www.emailjs.com/docs)
3.3 – 폼 전송 코드 작성하기
index.html의</body>바로 위에 아래 코드를 넣습니다.
<script type="text/javascript">
const form = document.getElementById("contact-form");
form.addEventListener("submit", function (event) {
event.preventDefault();
emailjs.sendForm("여기에_서비스_ID", "여기에_템플릿_ID", "#contact-form")
.then(function () {
console.log("전송 성공");
}, function (error) {
console.log("전송 실패", error);
});
});
</script>여기에_서비스_ID와여기에_템플릿_ID를 메모장에 적어 둔 값으로 바꿉니다.- 파일을 저장합니다.
addEventListener("submit", ...)는 폼의 [전송하기] 버튼을 눌렀을 때 실행할 동작을 정합니다.
event.preventDefault()는 폼의 기본 동작을 멈춥니다.
폼의 기본 동작은 데이터를 주소로 보내며 페이지를 새로 고치는 것입니다.
emailjs.sendForm은 값 세 개를 순서대로 받습니다.
서비스 ID, 템플릿 ID, 폼을 가리키는 선택자입니다.
#contact-form은 id가 contact-form인 요소를 가리키는 선택자입니다.
이 함수는 폼의 입력값을 name별로 모아 같은 이름의 템플릿 변수 자리에 넣어 EmailJS로 보냅니다.
event.preventDefault() 줄을 빠뜨리면 [전송하기]를 누르는 순간 페이지가 새로 고쳐집니다. 그러면 전송이 중간에 끊겨 메일이 가지 않습니다. 폼으로 메일을 보낼 때는 이 줄을 반드시 넣습니다.
3.4 – 전송 결과 화면에 알려 주기
- 3.3에서 넣은 script를 아래 내용으로 바꿉니다.
<script type="text/javascript">
const form = document.getElementById("contact-form");
const status = document.getElementById("status");
form.addEventListener("submit", function (event) {
event.preventDefault();
status.textContent = "보내는 중...";
emailjs.sendForm("여기에_서비스_ID", "여기에_템플릿_ID", "#contact-form")
.then(function () {
status.textContent = "메일을 보냈습니다.";
form.reset();
}, function (error) {
status.textContent = "전송에 실패했습니다. 잠시 후 다시 시도해 주세요.";
console.log(error);
});
});
</script>- 서비스 ID와 템플릿 ID를 다시 확인하고 파일을 저장합니다.
앞 단계 코드는 성공·실패를 console.log로만 남깁니다.
콘솔은 개발자 도구를 열어야 보이므로, 폼을 쓰는 사람은 결과를 알 수 없습니다.
버튼 아래 <p id="status">에 결과 문구를 띄워 사람이 바로 보게 만듭니다.
status.textContent는 <p id="status">의 글자를 바꿉니다.
보내기 전에는 보내는 중..., 성공하면 메일을 보냈습니다., 실패하면 안내 문구가 화면에 나타납니다.
form.reset()은 입력칸을 모두 비웁니다.
성공한 뒤 입력칸을 비우면 같은 내용을 실수로 다시 보내는 일을 막습니다.
이제 코드가 들어간 자리를 정리하면, index.html 한 파일 안에 SDK 두 줄은 </head> 위에, 폼 전송 script는 </body> 위에 있습니다.
3.5 – 브라우저에서 테스트하기
index.html을 저장하고, 브라우저에서 F5를 눌러 새로고침합니다.- [이름] 칸에
테스트를 입력합니다. - [이메일 주소] 칸에 본인 이메일 주소를 입력합니다.
- [제목] 칸에
테스트 문의를 입력합니다. - [내용] 칸에
잘 도착하는지 확인합니다를 입력합니다. - [전송하기] 버튼을 클릭합니다.
- 버튼 아래에
메일을 보냈습니다.가 나타나는지 확인합니다. - [To Email]로 지정한 본인 메일함을 열어 메일이 왔는지 확인합니다. 약 30초 안에 도착합니다.
도착한 메일의 제목에 입력한 제목이 들어 있고, 본문에 이름·이메일·내용이 템플릿 형식대로 채워져 있으면 폼과 템플릿 변수가 바르게 연결된 것입니다.
테스트에는 실제 다른 사람의 정보 대신 위와 같은 가상 값을 넣습니다.
이메일 주소만 본인 것을 넣어, 메일이 도착하는지 직접 확인합니다.
결과가 예상과 다르면 브라우저 개발자 도구의 콘솔을 엽니다. Windows·Linux는 F12, macOS는 Cmd+Option+I를 누른 뒤 [Console] 탭을 봅니다. 코드에 적은 console.log(error)가 실패 원인을 여기에 출력합니다.
4 – React 앱에서 이메일 보내기
4.1 – React 프로젝트 만들기
- 터미널(명령 프롬프트)을 엽니다.
- 프로젝트를 만들 위치로 이동합니다.
- 아래 명령을 입력하고 Enter를 누릅니다.
npm create vite@latest emailjs-react -- --template react- 만들어진 폴더로 이동해 패키지를 설치하고 개발 서버를 켭니다.
cd emailjs-react
npm install
npm run dev- 터미널에 나온 주소(기본값
http://localhost:5173)를 브라우저에서 엽니다.
Vite는 React 앱을 만들고 실행하는 도구입니다.
npm create vite@latest는 React 앱의 기본 폴더와 파일을 만듭니다.
npm run dev는 개발 서버를 켭니다.
개발 서버는 코드를 고칠 때마다 화면을 자동으로 다시 그려 주는, 내 컴퓨터에서만 도는 임시 웹 서버입니다.
브라우저에 Vite와 React 기본 페이지가 나타나면 준비가 된 것입니다.
여기서는 React 앱 도구로 Vite를 씁니다. 다른 도구를 써도 EmailJS를 붙이는 방법은 같고, 환경 변수 이름 앞에 붙이는 접두사만 달라집니다. (2026-09-02 확인, 출처 vite.dev, react.dev)
4.2 – EmailJS SDK 설치와 키 보관하기
- 개발 서버가 켜진 터미널은 그대로 두고, 새 터미널을 열어
emailjs-react폴더로 이동합니다. - EmailJS SDK를 설치합니다.
npm install @emailjs/browser- 프로젝트 맨 위 폴더에
.env.local파일을 만듭니다. - 아래 세 줄을 넣고, 오른쪽 값을 메모장에 적어 둔 실제 값으로 바꿉니다.
VITE_EMAILJS_SERVICE_ID=여기에_서비스_ID
VITE_EMAILJS_TEMPLATE_ID=여기에_템플릿_ID
VITE_EMAILJS_PUBLIC_KEY=여기에_공개_키- 파일을 저장합니다.
@emailjs/browser는 HTML에서 CDN으로 붙이던 그 SDK의 설치본입니다.
HTML에서는 script 태그로 붙였지만, React에서는 이렇게 설치해 import로 불러옵니다.
.env.local은 서비스 ID·템플릿 ID·공개 키처럼 배포마다 달라질 수 있는 값을 한곳에 모아 두는 파일입니다.
환경 변수는 이 파일에 적어 두고 코드에서 불러 쓰는 값입니다.
Vite에서는 이름이 VITE_로 시작하는 환경 변수만 코드에서 읽을 수 있습니다.
공개 키는 브라우저에 노출돼도 되는 값이므로 .env.local이 이 값을 숨겨 주지는 않습니다. 이 파일을 쓰는 까닭은 값을 코드 여기저기 흩지 않고 한곳에 모아, 배포 환경마다 쉽게 바꾸기 위해서입니다.
4.3 – 문의 폼 컴포넌트 만들기
src폴더에ContactForm.jsx파일을 만듭니다.- 아래 코드를 그대로 붙여 넣습니다.
import { useRef, useState } from "react";
import emailjs from "@emailjs/browser";
export default function ContactForm() {
const form = useRef();
const [status, setStatus] = useState("");
const sendEmail = (e) => {
e.preventDefault();
setStatus("보내는 중...");
emailjs
.sendForm(
import.meta.env.VITE_EMAILJS_SERVICE_ID,
import.meta.env.VITE_EMAILJS_TEMPLATE_ID,
form.current,
{ publicKey: import.meta.env.VITE_EMAILJS_PUBLIC_KEY }
)
.then(
() => {
setStatus("메일을 보냈습니다.");
form.current.reset();
},
(error) => {
setStatus("전송에 실패했습니다. 잠시 후 다시 시도해 주세요.");
console.log(error);
}
);
};
return (
<form ref={form} onSubmit={sendEmail}>
<label>이름</label>
<input type="text" name="user_name" required />
<label>이메일 주소</label>
<input type="email" name="user_email" required />
<label>제목</label>
<input type="text" name="subject" required />
<label>내용</label>
<textarea name="message" rows="5" required />
<button type="submit">전송하기</button>
<p>{status}</p>
</form>
);
}src/App.jsx파일을 열어 아래 내용으로 바꿉니다.
import ContactForm from "./ContactForm";
export default function App() {
return <ContactForm />;
}- 두 파일을 저장합니다.
컴포넌트는 화면 한 조각을 만들어 내는 함수입니다.
ContactForm은 문의 폼 한 조각을 그립니다.
useRef는 화면의 특정 요소를 가리키는 값을 만드는 도구입니다.
<form ref={form}>으로 연결하면 form.current가 그 폼을 가리킵니다.
sendForm의 세 번째 자리에 이 form.current를 넘깁니다.
HTML에서 선택자 #contact-form을 넘긴 것과 같은 일을, React에서는 이렇게 합니다.
각 입력칸의 name(user_name, user_email, subject, message)이 템플릿 변수와 같아야 하는 규칙은 HTML과 같습니다.
import.meta.env.VITE_EMAILJS_SERVICE_ID는 .env.local에 적어 둔 값을 불러옵니다.
useState로 만든 status는 전송 상태 문구를 담고, <p>{status}</p>에 그 값이 표시됩니다.
.env.local 파일을 만들거나 고친 뒤에는 개발 서버를 껐다 다시 켜야 바뀐 환경 변수가 반영됩니다. 개발 서버가 켜진 터미널에서 Ctrl+C로 끄고 npm run dev로 다시 켭니다.
4.4 – 개발 서버에서 테스트하기
- 개발 서버가 켜져 있는지 확인합니다. 꺼져 있으면
npm run dev로 켭니다. - 브라우저에서
http://localhost:5173을 엽니다. - [이름] 칸에
테스트를 입력합니다. - [이메일 주소] 칸에 본인 이메일 주소를 입력합니다.
- [제목] 칸에
테스트 문의를 입력합니다. - [내용] 칸에
잘 도착하는지 확인합니다를 입력합니다. - [전송하기] 버튼을 클릭합니다.
- 폼 아래에
메일을 보냈습니다.가 나타나는지 확인합니다. - [To Email]로 지정한 본인 메일함을 열어 메일이 왔는지 확인합니다. 약 30초 안에 도착합니다.
도착한 메일의 제목과 본문이 템플릿 형식대로 채워져 있으면 React 앱과 EmailJS가 바르게 연결된 것입니다.
여기서 확인하는 내용은 HTML 방식과 같습니다.
다른 점은 파일을 직접 여는 대신 개발 서버 주소로 연다는 것뿐입니다.
화면이 예상과 다르면 개발자 도구 콘솔(F12)에서 오류를 봅니다. 값이 undefined로 나오면 .env.local의 이름이 VITE_로 시작하는지, 그리고 개발 서버를 다시 켰는지 확인합니다.
5 – 완성 코드 한눈에 보기
5.1 – 방식을 골라 전체 코드 확인하기
- 아래에서 자기가 쓴 방식의 탭을 누릅니다. HTML 파일로 만들었으면 [HTML], React로 만들었으면 [React]입니다.
- 탭에 나온 전체 코드와 자기 코드를 견주어, 빠진 부분이 없는지 확인합니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>이메일 문의하기</title>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@emailjs/browser@4/dist/email.min.js"></script>
<script type="text/javascript">
emailjs.init({ publicKey: "여기에_공개_키" });
</script>
<style>
.email-form { max-width: 500px; margin: 20px auto; padding: 20px; border: 1px solid #ccc; border-radius: 8px; }
.form-group { margin-bottom: 15px; }
label { display: block; margin-bottom: 5px; font-weight: bold; }
input[type="text"], input[type="email"], textarea { width: 100%; padding: 8px; box-sizing: border-box; border: 1px solid #ddd; border-radius: 4px; }
button { background-color: #007bff; color: white; padding: 10px 15px; border: none; border-radius: 4px; cursor: pointer; }
button:hover { background-color: #0056b3; }
</style>
</head>
<body>
<div class="email-form">
<h2>이메일 보내기</h2>
<form id="contact-form">
<div class="form-group">
<label for="name">이름</label>
<input type="text" id="name" name="user_name" required>
</div>
<div class="form-group">
<label for="email">이메일 주소</label>
<input type="email" id="email" name="user_email" required>
</div>
<div class="form-group">
<label for="subject">제목</label>
<input type="text" id="subject" name="subject" required>
</div>
<div class="form-group">
<label for="message">내용</label>
<textarea id="message" name="message" rows="5" required></textarea>
</div>
<button type="submit">전송하기</button>
</form>
<p id="status"></p>
</div>
<script type="text/javascript">
const form = document.getElementById("contact-form");
const status = document.getElementById("status");
form.addEventListener("submit", function (event) {
event.preventDefault();
status.textContent = "보내는 중...";
emailjs.sendForm("여기에_서비스_ID", "여기에_템플릿_ID", "#contact-form")
.then(function () {
status.textContent = "메일을 보냈습니다.";
form.reset();
}, function (error) {
status.textContent = "전송에 실패했습니다. 잠시 후 다시 시도해 주세요.";
console.log(error);
});
});
</script>
</body>
</html>
import { useRef, useState } from "react";
import emailjs from "@emailjs/browser";
export default function ContactForm() {
const form = useRef();
const [status, setStatus] = useState("");
const sendEmail = (e) => {
e.preventDefault();
setStatus("보내는 중...");
emailjs
.sendForm(
import.meta.env.VITE_EMAILJS_SERVICE_ID,
import.meta.env.VITE_EMAILJS_TEMPLATE_ID,
form.current,
{ publicKey: import.meta.env.VITE_EMAILJS_PUBLIC_KEY }
)
.then(
() => {
setStatus("메일을 보냈습니다.");
form.current.reset();
},
(error) => {
setStatus("전송에 실패했습니다. 잠시 후 다시 시도해 주세요.");
console.log(error);
}
);
};
return (
<form ref={form} onSubmit={sendEmail}>
<label>이름</label>
<input type="text" name="user_name" required />
<label>이메일 주소</label>
<input type="email" name="user_email" required />
<label>제목</label>
<input type="text" name="subject" required />
<label>내용</label>
<textarea name="message" rows="5" required />
<button type="submit">전송하기</button>
<p>{status}</p>
</form>
);
}
이 탭은 지금까지 조각으로 넣은 코드를 방식별로 한 벌로 모은 것입니다.
HTML 코드는 index.html 한 파일에 든 전체이고, React 코드는 ContactForm.jsx 파일 하나입니다.
HTML 방식의 코드에서는 아래 세 자리를 메모장에 적어 둔 값으로 바꿔야 실제로 전송됩니다.
여기에_공개_키— Account 화면의 Public Key입니다.여기에_서비스_ID— 연결한 이메일 서비스의 Service ID입니다.여기에_템플릿_ID— 만든 템플릿의 Template ID입니다.
React 방식의 코드에는 이 세 자리 대신 import.meta.env로 값을 불러오는 부분이 있습니다.
값 자체는 4.2에서 만든 .env.local 파일에 두고, 이 컴포넌트를 화면에 띄우는 App.jsx 코드는 4.3에 있습니다.
6 – 전송이 안 될 때와 안전 점검
6.1 – 전송이 안 될 때 확인하기
- 화면에
전송에 실패했습니다가 나오면 개발자 도구 콘솔을 열어 오류 메시지를 봅니다. - 아래 표에서 오류에 맞는 원인을 찾아 고칩니다.
메일은 왔지만 값이 비어 있거나, 아예 전송이 실패하는 경우가 대부분입니다.
자주 나는 원인과 확인할 곳은 아래와 같습니다.
| 증상 | 원인과 확인할 곳 |
|---|---|
| 메일 본문의 이름·내용 자리가 비어 있음 | 폼 입력칸의 name과 템플릿 변수 이름이 다릅니다. 두 값을 대조합니다. |
| 콘솔에 Public Key 오류 | 코드에 넣은 공개 키에 오타가 있습니다. 메모한 값과 대조합니다. HTML은 emailjs.init에, React는 sendForm에 넣은 값입니다. |
| 콘솔에 서비스·템플릿 오류 | sendForm의 서비스 ID나 템플릿 ID에 오타가 있습니다. 메모한 값과 대조합니다. |
| 콘솔에 도메인 관련 오류(403 등) | 허용 도메인 제한에 현재 주소가 없습니다. 6.2의 허용 도메인 설정을 확인합니다. |
| 화면은 성공인데 메일이 안 옴 | 메일함의 스팸함을 확인하고, 템플릿의 [To Email] 주소가 맞는지 봅니다. |
한 자리를 고친 뒤 파일을 저장하고 다시 전송해 봅니다.
HTML 파일은 브라우저에서 F5로 새로고침해야 바뀐 코드가 반영되고, React 개발 서버는 저장하면 화면이 자동으로 다시 그려집니다.
6.2 – 공개 키 노출과 스팸 막기
- 왼쪽 메뉴에서 [Account]를 클릭하고 Security 화면을 엽니다.
- [Allowlist](허용 도메인) 항목에 문의 폼을 올린 사이트 주소를 추가합니다.
- 봇의 자동 전송을 막으려면 [reCAPTCHA] 항목을 켜고 안내에 따라 연결합니다.
이 실습의 코드는 브라우저에서 실행되므로, 공개 키와 폼 코드가 사이트를 여는 누구에게나 보입니다.
그래서 공개 사이트에 올릴 때는 위 세 가지를 함께 설정합니다.
Public Key(공개 키)는 브라우저 코드에 보여도 되는 값입니다.
공개를 전제로 만든 값이기 때문입니다.
반면 Private Key(비공개 키)는 발송 전체 권한을 가진 값이므로 브라우저 코드에 넣지 않습니다.
허용 도메인은 내 사이트에서 온 요청만 받도록 거르는 설정입니다.
이 설정을 해 두면 다른 사람이 내 공개 키를 복사해도 자기 사이트에서는 메일을 보내지 못합니다.
Private Key(비공개 키, access token)는 브라우저 코드에 절대 넣지 않습니다. 이 값이 노출되면 다른 사람이 내 계정으로 메일을 마음대로 보낼 수 있습니다. 브라우저에서 쓰는 값은 Public Key 하나입니다.
무료 플랜은 한 달에 200번까지만 발송됩니다. 봇이 폼을 자동으로 채워 스팸을 보내면 이 횟수가 빠르게 소진되고, 그 뒤에는 정상 문의도 발송되지 않습니다. 그래서 허용 도메인 제한과 reCAPTCHA로 자동 전송을 막습니다. (2026-09-02 확인, 출처 www.emailjs.com/pricing)
6.3 – 지금까지 한 작업 스스로 점검하기
지금까지 한 작업이 제대로 됐는지 아래 다섯 가지로 따져 봅니다. 세 가지 이상 맞으면 이 과정을 마친 것으로 봅니다.
- [전송하기]를 눌렀을 때 페이지가 새로 고쳐지지 않고 버튼 아래에 상태 문구가 나타납니다.
- 템플릿의 [To Email]로 지정한 메일함에 문의 메일이 도착합니다.
- 도착한 메일의 제목과 본문에 입력한 값이 템플릿 형식대로 채워져 있습니다.
- 입력칸을 비운 채 [전송하기]를 누르면 메일이 가지 않고 브라우저의 필수 입력 안내가 먼저 뜹니다.
- 브라우저 개발자 도구의 콘솔에 빨간 오류 메시지가 없습니다.