매장주인(관리자)이 가입하는 방식은 일반 유저와는 다르게 운영자가 수동으로 가입 승인 처리를 해주는 것이 일반적이다.(배달의민족 입점사나 당근마켓 동네업체 등록, 네이버 스마트 스토어 입점시 이렇게 한다.) 하지만 지금 프로젝트에는 운영자는 추후에 다시 논의해보기로 했기 때문에 운영자가 가입 승인처리를 하는 절차는 생략하기로 했다.
관리자 가입시, 일반 사용자보다 입력해야하는 정보가 더 많다. 관건은 사업자등록증 첨부인데 이미지파일을 어떻게 저장할지 찾아보았다.
aws의 S3를 사용하는 방법도 있고, 로컬서버에 저장하는 방법도 있었다. 그런데 사업자등록증 이미지에는 개인정보가 포함될 수 있고, public/uploads 폴더에 저장하면 누구나 접근 가능하므로 보안상 위험이 있다. 그래서, 이미지 파일을 디스크에 저장하지 않고, 용량의 부담이 있더라도 DB에 BLOB 형태로 저장하는 방법이 더 안전하다.
user 에서 사업자등록증을 추가해야되므로, String 이 아니라 Bytes 타입으로 수정해주고.
이전에 없던 Role 도 구분해주기 위해 enum 을 새로 만들고, user 에 추가해주었다.
기본값은 USER 이지만, isAdmin 이 true 라면 ADMIN이어야함.
1. user 수정, 추가
adminBusinessFile Bytes? // String? 에서 Bytes?로 수정
role Role @default(USER) // role 추가
2. enum Role 추가
enum Role {
USER
ADMIN
}
💡 차이점
newUser는 택배 박스에 적은 메모지. only 텍스트만 담긴다.
FormData는 택배 상자. 메모지(텍스트)도 넣을 수 있고, 파일(물건) 도 같이 넣어서 보낼 수 있음.
const newUser = {
userId: id,
pwd: password,
name,
phone,
email,
isAdmin,
adminPhoneNumber: adminPhone,
adminBusinessFile: businessFile,
// points는 회원가입 시 직접 입력하지 않음
};
이건 일반적인 JSON 객체.
서버로 보낼 때는 JSON.stringify(newUser) 해서 JSON 형식으로 보냄.
단점: 파일(binary 데이터)는 JSON으로 보낼 수 없음. (파일은 텍스트가 아니라 바이너리니까)
const formData = new FormData();
formData.append("userId", id);
formData.append("pwd", password);
formData.append("name", name);
formData.append("phone", phone);
formData.append("email", email);
formData.append("isAdmin", String(isAdmin));
if (isAdmin) {
formData.append("adminPhoneNumber", adminPhone);
formData.append("adminBusinessLicense", businessFile);
}
이건 multipart/form-data 형식.
주로 파일 업로드할 때 사용하는 방식.
이 방식은 텍스트 데이터랑 파일(binary) 데이터를 같이 서버로 보낼 수 있음.
try {
const response = await fetch("/api/signup", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(newUser),
});
try {
const response = await fetch("/api/signup", {
method: "POST",
body: formData,
});
‼️npm install formidable 필수!
Next.js API는 기본적으로 multipart/form-data를 처리 못 하므로 formidable 등의 라이브러리를 사용해야 한다.
adminBusinessFileBuffer
이 변수는 "관리자 회원가입" 시 첨부하는 사업자등록증 파일의 실제 데이터(바이트)를 담는 역할.
즉, 사용자가 파일을 업로드하면, 그 파일을 서버에서 읽어서 DB에 저장할 때 사용.
readFile
readFile은 Node.js의 fs/promises 모듈에서 제공하는 함수!
파일 경로(예: C:/.../tmp/upload_xxx.png)를 받아서, 파일의 내용을 메모리(버퍼)로 읽어온다.
예시:
adminBusinessFileBuffer = await readFile(filePath);
이 코드는 해당 경로의 파일을 읽어서, 그 내용을 변수에 저장한다.
예시: files.adminBusinessFile[0].filepath
→ 업로드된 파일의 실제 경로(서버 임시폴더 위치)
전체 흐름 요약
사용자가 회원가입 폼에서 사업자등록증 파일을 첨부함
서버에서 formidable로 파일을 파싱 → files.adminBusinessFile[0]에 파일 정보가 들어감
readFile로 해당 파일 경로의 내용을 읽어서 adminBusinessFileBuffer에 저장
이 버퍼(파일 데이터)를 DB에 저장
정리
adminBusinessFileBuffer: 파일의 실제 데이터(바이트)를 담는 변수
readFile: 파일 경로에서 파일 내용을 읽어오는 함수
[0]: 파일이 배열로 오기 때문에 첫 번째(실제 업로드된) 파일을 선택하는 것

문제1. 오타 append 를 appent 로 작성함
문제2. import { readFile } from "fs/promises"; 하지 않음
-- is not defined 류의 에러메세지는 주로 import 가 제대로 안됬을때 발생하는 오류다.
나중에도 이런 비슷한 메세지가 뜨면, import가 올바로 되어있는지 확인해보자.
unhandledRejection: ReferenceError: readFile is not defined

MySQL에서 SELECT * FROM USER; 하면, 새로 회원가입된 black은 isAdmin 이 1이고 adminPhoneNumber 잘 기입되어있다. 그리고 가장 중요한 adminBusinessFile 에 BLOB으로 저장된것을 확인 할 수 있다.