Commit 09cfa64e authored by Kim, MinGyu's avatar Kim, MinGyu
Browse files

이미지 미리보기 및 저장

parent 39f40906
...@@ -2,3 +2,4 @@ node_modules/ ...@@ -2,3 +2,4 @@ node_modules/
.vscode/ .vscode/
package-lock.json package-lock.json
dist/ dist/
upload
\ No newline at end of file
...@@ -4,4 +4,12 @@ import baseUrl from "./baseUrl"; ...@@ -4,4 +4,12 @@ import baseUrl from "./baseUrl";
export const profile = async () => { export const profile = async () => {
const { data } = await axios.get(`${baseUrl}/profile`); const { data } = await axios.get(`${baseUrl}/profile`);
return data; return data;
}; };
\ No newline at end of file
export const picture = async (formdata: FormData) => {
await axios.post(`${baseUrl}/profile`, formdata);
};
export const nickname = async (formdata: FormData) => {
await axios.post(`${baseUrl}/profile`, formdata);
};
import { authApi } from "../apis"; import { authApi } from "../apis";
import { IUser, Profile } from "../types"; import { IUser } from "../types";
import { profileApi } from "../apis";
const LOCAL_USER_INFO = "survey-user-info"; const LOCAL_USER_INFO = "survey-user-info";
...@@ -60,7 +59,3 @@ export const getLocalUser = () => { ...@@ -60,7 +59,3 @@ export const getLocalUser = () => {
return user; return user;
}; };
export const handleProfile = async () => {
const user : Profile = await profileApi.profile();
return user
};
\ No newline at end of file
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { handleProfile } from "./auth.helper"; import { Profile } from "../types";
import { profileApi } from "../apis";
export default function Profile() { export default function Profile() {
// 로컬 저장소에는 로그인 여부만 저장 // 로컬 저장소에는 로그인 여부만 저장
const [email, setemail] = useState(""); const [email, setEmail] = useState("");
const [picturename, setPicturename] = useState("");
const [file, setFile] = useState<File>();
const [imageSrc, setImageSrc] = useState("");
const [nickname, setNickname] = useState("");
const emailChange = async () => { const handleProfile = async () => {
const profile = await handleProfile(); const user: Profile = await profileApi.profile();
return user;
};
const PictureSrc = (fileBlob: Blob) => {
const reader = new FileReader();
reader.readAsDataURL(fileBlob);
reader.onload = (data) => {
if (typeof data.target?.result === "string") {
console.log(data.target?.result);
setImageSrc(data.target?.result);
}
};
};
setemail(profile.email); const onUploadFile = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!(file === undefined)) {
setFile(file);
PictureSrc(file);
}
};
const userChange = async () => {
const profile = await handleProfile();
setEmail(profile.email);
setPicturename(profile.newfilename);
};
const handleClick = async (
e: React.MouseEvent<HTMLButtonElement, globalThis.MouseEvent>
) => {
const formdata = new FormData();
if (!(file === undefined || file === null)) {
formdata.append("picture", file);
}
await profileApi.picture(formdata);
}; };
useEffect(() => { useEffect(() => {
emailChange(); userChange();
}, []); }, []);
return ( return (
<div className="grid "> <div className="grid ">
<div className="ml-20 mt-10">프로필 </div> <form className="">
<div className="grid m-20 border-0 border-y-2"> <div className="ml-40 mt-10">프로필 </div>
<div className="grid ml-40 mt-20 border-0 border-y-2 w-2/3">
<div className="flex"> <div className="flex">
<div className="py-10 basis-1/5 border-0 border-r-2 bg-gray-100 grid place-items-center "> <div className="py-10 basis-1/5 border-0 border-r-2 bg-gray-100 grid place-items-center ">
Email Email
</div> </div>
<div className="basis-full">{email}</div> <div className="basis-full my-5 p-5">{email}</div>
</div> </div>
<div className="flex border-0 border-t-2"> <div className="flex border-0 border-t-2">
<div className="py-10 basis-1/5 border-0 border-r-2 bg-gray-100 grid place-items-center"> <div className="py-10 basis-1/5 border-0 border-r-2 bg-gray-100 grid place-items-center">
사진 사진
</div> </div>
<div className="basis-full">a</div> <div className="basis-full p-2">
{imageSrc ? (
<img
src={imageSrc}
width={200}
height={200}
alt="preview-img"
/>
) : (
<img
src={"http://localhost:3000/" + picturename}
width={200}
height={200}
/>
)}
<input
type="file"
id="files"
className="hidden"
onChange={onUploadFile}
></input>
<label htmlFor="files" className="border-2">
이미지 선택
</label>
</div>
</div> </div>
<div className="flex border-0 border-t-2"> <div className="flex border-0 border-t-2">
<div className="py-10 basis-1/5 border-0 border-r-2 bg-gray-100 grid place-items-center"> <div className="py-10 basis-1/5 border-0 border-r-2 bg-gray-100 grid place-items-center">
별명 별명
</div> </div>
<div className="basis-full">a</div> <div className="basis-full ">
<input
placeholder="빈칸"
className="basis-full placeholder:text-black my-10 ml-5"
/>
</div>
</div>
</div> </div>
<div className="grid justify-items-center mb-20">
<button onClick={handleClick} className=" border-2 ">
저장하기
</button>
</div> </div>
</form>
</div> </div>
); );
} }
import { Files } from "formidable";
export interface IUser { export interface IUser {
email?: string; email?: string;
isLoggedIn: boolean; isLoggedIn: boolean;
...@@ -28,4 +30,8 @@ export interface SignupUser { ...@@ -28,4 +30,8 @@ export interface SignupUser {
export interface Profile { export interface Profile {
_id: string; _id: string;
email: string; email: string;
originalfilename: string;
newfilename: string;
picturepath: string;
nickname: string;
} }
...@@ -33,10 +33,12 @@ ...@@ -33,10 +33,12 @@
"typescript": "^4.7.4" "typescript": "^4.7.4"
}, },
"dependencies": { "dependencies": {
"@types/formidable": "^2.0.5",
"axios": "^0.27.2", "axios": "^0.27.2",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"express": "^4.18.1", "express": "^4.18.1",
"formidable": "^2.0.1",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"mongoose": "^6.4.2", "mongoose": "^6.4.2",
"validator": "^13.7.0" "validator": "^13.7.0"
......
...@@ -8,9 +8,10 @@ app.use(express.json()); ...@@ -8,9 +8,10 @@ app.use(express.json());
app.use(express.urlencoded({ extended: true })); app.use(express.urlencoded({ extended: true }));
app.use(cookieParser()); app.use(cookieParser());
app.get('/', (req, res) => res.send('Hello World! 안녕하세요'))
app.use("/api", router); app.use("/api", router);
app.use(express.static(__dirname + "/upload"));
app.use((err: any, req: Request, res: Response, next: NextFunction) => { app.use((err: any, req: Request, res: Response, next: NextFunction) => {
console.log("익스프레스 에러: ", err); console.log("익스프레스 에러: ", err);
res.status(err.statusCode || 500).send(err.message || "서버 에러"); res.status(err.statusCode || 500).send(err.message || "서버 에러");
......
import { userDb } from "../db"; import { userDb } from "../db";
import { asyncWrap } from "../helpers/asyncWrap"; import { asyncWrap } from "../helpers/asyncWrap";
import { Request } from "express"; import { Request } from "express";
import formidable, { Fields, Files } from "formidable";
import { ObjectId } from "mongoose";
import fs from "fs";
export interface TypedRequestAuth<T> extends Request { export interface TypedRequestAuth<T> extends Request {
auth: T; auth: T;
...@@ -19,9 +22,32 @@ export const createUser = asyncWrap(async (req, res) => { ...@@ -19,9 +22,32 @@ export const createUser = asyncWrap(async (req, res) => {
}); });
export const getProfile = asyncWrap(async (reqExp, res) => { export const getProfile = asyncWrap(async (reqExp, res) => {
const req = reqExp as TypedRequestAuth<{userId : string}>; // 앞에서는 토큰으로써 사용하기 때문에 JwtPayload 를 사용하고 여기서는 verify 에서 토큰을 디코딩했기에 ObjectId 타입의 string으로 바뀌게 된다. const req = reqExp as TypedRequestAuth<{ userId: string }>; // 앞에서는 토큰으로써 사용하기 때문에 JwtPayload 를 사용하고 여기서는 verify 에서 토큰을 디코딩했기에 ObjectId 타입의 string으로 바뀌게 된다.
const {userId} = req.auth; const { userId } = req.auth;
const profile = await userDb.getProfile(userId); const profile = await userDb.getProfile(userId);
res.json(profile) res.json(profile);
}) });
\ No newline at end of file
export const postPicture = asyncWrap(async (reqExp, res) => {
const req = reqExp as TypedRequestAuth<{ userId: ObjectId }>;
const { userId } = req.auth;
const form = formidable({
uploadDir: "./src/upload",
keepExtensions: true,
multiples: false,
});
form.parse(req, (err, fields, files) => {
if (!Array.isArray(files.picture)) {
//파일 좁히기 중
const originalfilename = files.picture.originalFilename;
const newfilename = files.picture.newFilename;
const picturepath = files.picture.filepath;
userDb.postPicture(userId, originalfilename, newfilename, picturepath);
}
});
res.json();
});
import bcrypt from "bcryptjs"; import bcrypt from "bcryptjs";
import { Files } from "formidable";
import { ObjectId } from "mongoose";
import { IUser, User } from "../models"; import { IUser, User } from "../models";
export const createUser = async (user: IUser) => { export const createUser = async (user: IUser) => {
...@@ -39,7 +41,30 @@ export const isUser = async (email: string) => { ...@@ -39,7 +41,30 @@ export const isUser = async (email: string) => {
} }
}; };
export const getProfile = async (userId : string) => { export const getProfile = async (userId: string) => {
const profile = await User.findById(userId) const profile = await User.findById(userId);
return profile //이름 수정 return profile; //이름 수정
} };
\ No newline at end of file
export const postPicture = (
userId: ObjectId,
originalfilename: string | null,
newfilename: string,
picturepath: string
) => {
User.findByIdAndUpdate(
userId,
{
originalfilename: originalfilename,
newfilename: newfilename,
picturepath: picturepath,
},
function (err: any, docs: any) {
if (err) {
console.log(err);
} else {
console.log("Updated User : ", docs);
}
}
);
};
import { Document, model, Schema, Types } from "mongoose"; import { Document, model, Schema, Types } from "mongoose";
import { Posting } from "."; // import { Posting } from ".";
export interface PostType { export interface PostType {
title: string; title: string;
......
...@@ -5,6 +5,10 @@ export interface IUser { ...@@ -5,6 +5,10 @@ export interface IUser {
name?: string; name?: string;
password: string; password: string;
role?: Types.ObjectId; role?: Types.ObjectId;
originalfilename?: string;
newfilename?: string;
picturepath?: string;
nickname?: string;
} }
const validateEmail = (email: string) => { const validateEmail = (email: string) => {
...@@ -21,6 +25,10 @@ const schema = new Schema<IUser>( ...@@ -21,6 +25,10 @@ const schema = new Schema<IUser>(
validate: [validateEmail, "이메일을 입력해주세요"], validate: [validateEmail, "이메일을 입력해주세요"],
}, },
name: { type: String }, name: { type: String },
originalfilename: { type: String },
newfilename: { type: String },
nickname: { type: String },
picturepath: { type: String },
password: { type: String, required: true, select: false }, password: { type: String, required: true, select: false },
role: { type: Schema.Types.ObjectId, ref: "Role" }, role: { type: Schema.Types.ObjectId, ref: "Role" },
}, },
......
...@@ -3,8 +3,8 @@ import { userCtrl, authCtrl } from "../controllers"; ...@@ -3,8 +3,8 @@ import { userCtrl, authCtrl } from "../controllers";
const router = express.Router(); const router = express.Router();
router router.route("/").get(authCtrl.requireLogin, userCtrl.getProfile);
.route("/")
.get(authCtrl.requireLogin, userCtrl.getProfile) router.route("/").post(authCtrl.requireLogin, userCtrl.postPicture); //중간에 req쪽에 fields와 files 넣는 미들웨어 추가
export default router; export default router;
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment