Commit 85077051 authored by Lee Soobeom's avatar Lee Soobeom
Browse files

Merge remote-tracking branch 'origin/mk8' into develop0

parents 483dba83 fc01d0b3
import React from "react"; import React, { useState } from "react";
import Theme from "./theme";
import { PostType } from "./typesrc";
import axios from "axios";
function Title() {
const [title, setTitle] = useState<string>("질문종류");
function TitleChange(e: { target: { value: React.SetStateAction<string> } }) {
setTitle(e.target.value);
}
}
function Body() {
const [body, setBody] = useState<string>("질문종류");
function BodyChange(e: { target: { value: React.SetStateAction<string> } }) {
setBody(e.target.value);
}
}
function SelectCity() {
const [selectCity, setSelectCity] = useState<string>("질문종류");
function CityChange(e: { target: { value: React.SetStateAction<string> } }) {
setSelectCity(e.target.value);
}
return (
<select
id="Questions"
className="border border-3 border-black w-1/12"
onChange={CityChange}
defaultValue="질문종류"
>
<option value="질문종류">도시</option>
<option value="Seoul">서울</option>
<option value="Busan">부산</option>
<option value="Incheon">인천</option>
<option value="Daegoo">대구</option>
<option value="Kwangjoo">광주</option>
<option value="Daejeon">대전</option>
<option value="Woolsan">울산</option>
<option value="Sejong">세종</option>
<option value="Dokdo">독도</option>
<option value="Jeju">제주</option>
</select>
);
}
function SelectTheme() {
const [selectTheme, setSelectTheme] = useState<string>("질문종류");
function ThemeChange(e: { target: { value: React.SetStateAction<string> } }) {
setSelectTheme(e.target.value);
}
return (
<select
id="Questions"
className="border border-3 border-black w-1/12"
onChange={ThemeChange}
defaultValue="질문종류"
>
<option value="질문종류">테마</option>
<option value="cycling">사이클링</option>
<option value="surfing">서핑</option>
<option value="activity">액티비티</option>
<option value="camping">캠핑</option>
<option value="sking">스키</option>
<option value="boat">보트</option>
<option value="desert">사막</option>
<option value="golf">골프</option>
<option value="cave">동굴</option>
<option value="history">문화재</option>
<option value="zoo">동물원</option>
<option value="cycling">사이클링</option>
<option value="cycling">{selectTheme}</option>
</select>
);
}
// 눌렀다는 데이터가 어딘가에 있어야 한다. Map 객체를 이용해서 기타등등
// function postup() {
// axios.post("localhost:3000/api/post/up", {
// id: "a",
// title: title,
// body: body,
// date: `${() => new Date()}`,
// theme: selectTheme,
// city: selectCity,
// });
// }
export default function Posting() { export default function Posting() {
return( return (
<div className="flex flex-col border-3"> <div className="flex flex-col border-3">
<form className="w-full items-center" > <form className="w-full items-center">
<div className="flex flex-row relative"> <div className="flex flex-row relative">
<p className="basis-4/12 place-self-center">Id</p> <p className="basis-1/12 gap-x-8">Id</p>
<p className="basis-8/12 invisible">empty</p> <p className="basis-8/12 invisible">empty</p>
<SelectCity />
<button data-dropdown-toggle="dropdownId"className="basis-1/12">Theme</button> <SelectTheme />
<p className="basis-1/12">city</p>
<button className="basis-1/12 ">글쓰기</button> <button
type="submit"
</div> className="border border-black basis-1/12 gap-x-8"
<div className="flex border-4"> >
<textarea placeholder="title" className="w-full h-8"></textarea> 글쓰기
</div> </button>
<div className="flex border-2"> </div>
<textarea placeholder="body" className="w-full h-96"></textarea>
</div> <div className="flex border-4">
</form> <textarea
onChange={Title}
placeholder="title"
className="w-full h-8"
></textarea>
</div>
<div onChange={Body} className="flex border-2">
<textarea placeholder="body" className="w-full h-96"></textarea>
</div> </div>
); </form>
} </div>
\ No newline at end of file );
}
...@@ -7,4 +7,4 @@ export interface PostType { ...@@ -7,4 +7,4 @@ export interface PostType {
theme?: string; theme?: string;
city?: string; city?: string;
} }
\ No newline at end of file
...@@ -4,68 +4,116 @@ import { PostType } from "./typesrc"; ...@@ -4,68 +4,116 @@ import { PostType } from "./typesrc";
import Post from "./post"; import Post from "./post";
function range(start: number, end: number) { function range(start: number, end: number) {
return (new Array(end - start + 1)).fill(undefined).map((_, i) => i + start); return new Array(end - start + 1).fill(undefined).map((_, i) => i + start);
} }
interface Posts { interface Posts {
posts: PostType[]; posts: PostType[];
} }
export const fakes = [ export const fakes = [
{ id: "a", title: '여행가고싶다...', date: '2022-06-30', counts: 0 }, {
{ id: "b", title: '바다!바다!바다!', date: '2022-08-01', counts: 0 }, id: "a",
{ id: "c", title: 'Jeju-island', date: '2022-9-10', counts: 0 }, title: "여행가고싶다...",
{ id: "d", title: '마! 부싼 가봤나!', date: '2022-9-22', counts: 0 }, date: "2022-06-30",
{ id: "e", title: 'Daegu', date: '2022-10-1', counts: 0 }, counts: 0,
{ id: "f", title: '강원도 감자는 맛있다.', date: '2022-12-12', counts: 0 }, theme: "surfing",
{ id: "g", title: '부산남자의 서울여행', date: '2022-12-25', counts: 0 } city: "seoul",
},
{
id: "b",
title: "바다!바다!바다!",
date: "2022-08-01",
counts: 0,
theme: "surfing",
city: "seoul",
},
{
id: "c",
title: "Jeju-island",
date: "2022-9-10",
counts: 0,
theme: "surfing",
city: "seoul",
},
{
id: "d",
title: "마! 부싼 가봤나!",
date: "2022-9-22",
counts: 0,
theme: "surfing",
city: "seoul",
},
{
id: "e",
title: "Daegu",
date: "2022-10-1",
counts: 0,
theme: "surfing",
city: "seoul",
},
{
id: "f",
title: "강원도 감자는 맛있다.",
date: "2022-12-12",
counts: 0,
theme: "surfing",
city: "seoul",
},
{
id: "g",
title: "부산남자의 서울여행",
date: "2022-12-25",
counts: 0,
theme: "surfing",
city: "seoul",
},
]; ];
export default function BoardPage() { export default function BoardPage() {
const [posts, setPosts] = useState<PostType[]>(fakes);
const [posts, setPosts] = useState<PostType[]>(fakes); const titleHandleClick = (event: MouseEvent<HTMLButtonElement>) => {
const postId = event.currentTarget.id;
const newposts = [...posts];
newposts.forEach((post) => {
if (post.id === postId) {
post.counts = post.counts + 1;
return;
}
});
setPosts(newposts);
};
const titleHandleClick = (event: MouseEvent<HTMLButtonElement>) => { return (
const postId = event.currentTarget.id <div className="flex flex-col items-center">
const newposts = [...posts] <div className="flex flex-col items-center mt-6">
newposts.forEach(post => { <div>`Travel Report's Board`</div>
if (post.id === postId) { <div>`여행지 후기를 남겨주세요!`</div>
post.counts = post.counts + 1 </div>
return
}
})
setPosts(newposts)
}
<div className="flex flex-col w-10/12 mt-16">
return ( <div className="flex justify-end">
<div className="flex flex-col items-center"> <div className="border-2 mb-2">
<div className="flex flex-col items-center mt-6"> <Link to="/posting">
<div> <button>글쓰기+</button>
`Travel Report's Board` </Link>
</div> </div>{" "}
<div> {/* Link */}
`여행지 후기를 남겨주세요!`
</div>
</div>
<div className="flex flex-col w-10/12 mt-16">
<div className="flex justify-end">
<div className="border-2 mb-2"><Link to="/posting"><button>글쓰기+</button></Link></div> {/* Link */}
</div>
<div className="sm:overflow-y-scroll">
<div className="flex flex-row divide-x-2 border-2 border-solid bg-gray-500 border-y-2 h-10 ">
<div className="basis-full">Title</div>
<div className="basis-3/12">Date</div>
<div className="basis-2/12">Clicks</div>
</div>
<div>
{posts.map((post) => (
<Post key={post.id} post={post} handleClick={titleHandleClick} />
))}
</div>
</div>
</div>
</div> </div>
); <div className="sm:overflow-y-scroll">
} <div className="flex flex-row divide-x-2 border-2 border-solid bg-gray-500 border-y-2 h-10 ">
\ No newline at end of file <div className="basis-full">Title</div>
<div className="basis-3/12">Date</div>
<div className="basis-2/12">Clicks</div>
</div>
<div>
{posts.map((post) => (
<Post key={post.id} post={post} handleClick={titleHandleClick} />
))}
</div>
</div>
</div>
</div>
);
}
...@@ -14,6 +14,7 @@ export default function Header() { ...@@ -14,6 +14,7 @@ export default function Header() {
<div className="flex flex-row-reverse"> <div className="flex flex-row-reverse">
<button className="px-5 py-2 bg-teal-400 rounded"> <button className="px-5 py-2 bg-teal-400 rounded">
<Link to="/login" className="hover:bg-teal-100 focus:text-purple-500 ">Login</Link> <Link to="/login" className="hover:bg-teal-100 focus:text-purple-500 ">Login</Link>
</button> </button>
<button className="px-5 py-2 bg-purple-400 rounded"> <button className="px-5 py-2 bg-purple-400 rounded">
<Link to="/board" className="hover:bg-purple-300 focus:text-purple-500 ">Board</Link> <Link to="/board" className="hover:bg-purple-300 focus:text-purple-500 ">Board</Link>
......
import { Link, } from "react-router-dom"; import { Link } from "react-router-dom";
import React, { useState } from "react"; import React, { useState, FormEventHandler } from "react";
interface login { interface login {
id : string; id: string;
password : string; password: string;
} }
const fake = {id : "asdf", password : "qwer"} // const fake = { id: "asdf", password: "qwer" };
function Logindata(fake :login){ function Logindata() {
const [id, setId] = useState(""); const [id, setId] = useState("");
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
function login() {
return( <div className="flex flex-col md:w-2/3 md:gap-2"> fetch(`http://localhost:3000/api/auth/login`, {
method: "POST",
<input className="placeholder:text-slate-300
bg-white border border-slate-500 rounded-2xl body: JSON.stringify({
py-2 pl-9 pr-3 email: `${id}`,
focus:border-black password: `${password}`,
" placeholder="Id" type="text" name="Id" }),
onChange={(e) => setId(e.target.value)} /> }).then((response) => {
<input className="placeholder:italic placeholder:text-slate-300 console.log(response.json());
bg-white border border-slate-500 rounded-2xl });
py-2 pl-9 pr-3 }
focus:border-black return (
" placeholder="Password" type="text" name="Password" <div className="flex flex-col md:w-2/3 md:gap-2">
onChange={(e) => setPassword(e.target.value)} /> <input
className="placeholder:text-slate-300
bg-white border border-slate-500 rounded-2xl
py-2 pl-9 pr-3
focus:border-black
"
placeholder="Id"
type="text"
name="Id"
onChange={(e) => setId(e.target.value)}
/>
<input
className="placeholder:italic placeholder:text-slate-300
bg-white border border-slate-500 rounded-2xl
py-2 pl-9 pr-3
focus:border-black
"
placeholder="Password"
type="password"
name="Password"
onChange={(e) => setPassword(e.target.value)}
/>
<button
type="submit"
className="md:w-1/3 bg-sky-600 hover:bg-sky-700 rounded-xl"
onClick={login}
>
<Link to={"/"}>login</Link>
</button>
</div> </div>
);
)
} }
export default function Login() { export default function Login() {
return ( return (
<div>
<div> {/* <form onSubmit={loginsubmit}> */}
{/* <form onSubmit={loginsubmit}> */} <div className="flex flex-row grid grid-rows-2">
<div className="flex flex-row grid grid-rows-2"> <div className=" p-12 w-1/2 h-1/2 md:w-40 md:h-40 bg-red-400 place-self-center rounded-2xl">
<Link to="/">Travel Report</Link>
<div className=" p-12 w-1/2 h-1/2 md:w-40 md:h-40 bg-red-400 place-self-center rounded-2xl"> </div>
<Link to="/">Travel Report</Link>
</div> <div className=" flex-row w-auto h-60 md:w-1/2 bg-white border-2 border-black grid place-items-center rounded-xl place-self-center">
<div className="flex flex-col w-full md:flex-row md:p-20 md:gap-10">
<div className=" flex-row w-auto h-60 md:w-1/2 bg-white border-2 border-black grid place-items-center rounded-xl place-self-center"> <Logindata />
</div>
<div className="flex flex-col w-full md:flex-row md:p-20 md:gap-10"> <div className="flex-row grid grid-cols-3">
<button className="bg-white bottom-0 right-0">
<Logindata id={""} password={""} /> <Link to="/signup">회원가입</Link>
</button>
<button type="submit" className="md:w-1/3 bg-sky-600 hover:bg-sky-700 rounded-xl"> <div></div>
login <button className="bg-white inset-x-0">
</button> <Link to="/forgot">비밀번호 찾기</Link>
</div> </button>
<div className="flex-row grid grid-cols-3"> </div>
<button className="bg-white bottom-0 right-0"> </div>
<Link to="/signup">회원가입</Link> </div>
</button> {/* </form> */}
<div></div> </div> // Login Page
<button className="bg-white inset-x-0"> );
<Link to="/forgot">비밀번호 찾기</Link> }
</button>
</div>
</div>
</div>
{/* </form> */}
</div> // Login Page
);
}
\ No newline at end of file
...@@ -8,6 +8,8 @@ app.use(express.json()); ...@@ -8,6 +8,8 @@ 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((err: any, req: Request, res: Response, next: NextFunction) => { app.use((err: any, req: Request, res: Response, next: NextFunction) => {
console.log("익스프레스 에러: ", err); console.log("익스프레스 에러: ", err);
......
...@@ -25,15 +25,15 @@ export const login = asyncWrap(async (req, res) => { ...@@ -25,15 +25,15 @@ export const login = asyncWrap(async (req, res) => {
return res.status(401).send("잘못된 비밀번호를 입력하셨습니다"); return res.status(401).send("잘못된 비밀번호를 입력하셨습니다");
} }
// 3) 비밀번호가 맞으면 토큰 생성 // 3) 비밀번호가 맞으면 토큰 생성
const token = jwt.sign({ userId: user.id }, jwtCofig.secret, { const token = jwt.sign({ userId: user.id }, jwtCofig.secret, { //userId를 토큰에다 넣는 중.
expiresIn: jwtCofig.expires, expiresIn: jwtCofig.expires,
}); });
// 4) 토큰을 쿠키에 저장 // 4) 토큰을 쿠키에 저장
res.cookie(cookieConfig.name, token, { res.cookie(cookieConfig.name, token, {//token은 쿠키에 무엇을 실렸는가 이다. 항상 갖고 있다가 홈페이지 들어가면 서버로 접속
maxAge: cookieConfig.maxAge, maxAge: cookieConfig.maxAge,// 이 기간 한에서만 유효
path: "/", path: "/",//어떠한 경로에 관해서만 쓴다. 지금은 전부에 쓴다.
httpOnly: envConfig.mode === "production", httpOnly: envConfig.mode === "production", //false 면 브라우저에서 쿠키를 조작, true면 조작할 수 있다.
secure: envConfig.mode === "production", secure: envConfig.mode === "production", //true 면 https 를 통해서만 쿠키 전달, false면
}); });
// 5) 사용자 반환 // 5) 사용자 반환
res.json({ res.json({
...@@ -51,15 +51,15 @@ export const requireLogin = asyncWrap(async (reqExp, res, next) => { ...@@ -51,15 +51,15 @@ export const requireLogin = asyncWrap(async (reqExp, res, next) => {
const req = reqExp as TypedRequestAuth<string | JwtPayload>; const req = reqExp as TypedRequestAuth<string | JwtPayload>;
try { try {
// 1) 쿠키 토큰 존재 여부 확인 // 1) 쿠키 토큰 존재 여부 확인
const token = req.cookies[cookieConfig.name]; const token = req.cookies[cookieConfig.name]; //클라이언트 쪽에서 보낸 토큰을 받는중
if (!token) { if (!token) {
throw new Error("토큰이 존재하지 않습니다"); throw new Error("토큰이 존재하지 않습니다");
} }
// 2) 쿠키 유효성 검사 // 2) 쿠키 유효성 검사
const decodedUser = jwt.verify(token, jwtCofig.secret); const decodedUser = jwt.verify(token, jwtCofig.secret); // 아까보낸 토근을 디코딩중.
// 3) 요청 객체에 토큰 사용자 객체 추가 // 3) 요청 객체에 토큰 사용자 객체 추가
req.auth = decodedUser; req.auth = decodedUser;
next(); next();// 에러가 안나오면 next 사용, 나오면 catch쪽으로.
} catch (error) { } catch (error) {
res.clearCookie(cookieConfig.name); res.clearCookie(cookieConfig.name);
console.log("error in requreLogin===\n", error); console.log("error in requreLogin===\n", error);
......
export * as userCtrl from "./user.controller"; export * as userCtrl from "./user.controller";
export * as authCtrl from "./auth.controller"; export * as authCtrl from "./auth.controller";
export * as postCtrl from "./post.controller"; export * as postCtrl from "./post.controller";
\ No newline at end of file
...@@ -51,4 +51,4 @@ export const posting = asyncWrap(async (req, res) => { ...@@ -51,4 +51,4 @@ export const posting = asyncWrap(async (req, res) => {
// 8) 사용자 반환 // 8) 사용자 반환
res.json({}); res.json({});
}); });
\ No newline at end of file
export * as userDb from "./user.db"; export * as userDb from "./user.db";
export * as postDb from "./post.db"; export * as postDb from "./post.db";
import { PostType, Post } from "../models";
export const createPost = async (post: PostType) => {
const newPost = await Post.create(post);
return newPost;
};
export const checkTitleNull = async ( export const checkTitleNull = async (
title : string, title : string,
...@@ -36,3 +44,4 @@ export const selectCity = async ( ...@@ -36,3 +44,4 @@ export const selectCity = async (
} }
} }
...@@ -5,6 +5,7 @@ import { mongoUri } from "./config"; ...@@ -5,6 +5,7 @@ import { mongoUri } from "./config";
connect(mongoUri) connect(mongoUri)
.then((mgs) => { .then((mgs) => {
console.log(`Mongoose is connected with version: ${mgs.version}`); console.log(`Mongoose is connected with version: ${mgs.version}`);
app.listen(3000, () => { app.listen(3000, () => {
console.log(`server is running on port ${3000}`); console.log(`server is running on port ${3000}`);
}); });
......
export { default as User, IUser } from "./user.model"; export { default as User, IUser } from "./user.model";
export { default as Post, PostType } from "./post.model";
import { model, Schema } from "mongoose";
export interface PostType {
id: string;
title: string;
date: Date;
body : string;
counts?: number;
theme: string;
city: string;
}
const schema = new Schema<PostType>({
id : {type: String},
title : { type: String },
date : {type: Date},
body : {type : String},
counts : {type: Number},
theme : {type: String},
city : {type: String},
});
export default model<PostType>("Post", schema);
...@@ -14,7 +14,7 @@ const validateEmail = (email: string) => { ...@@ -14,7 +14,7 @@ const validateEmail = (email: string) => {
const schema = new Schema<IUser>({ const schema = new Schema<IUser>({
email: { email: {
type: String, type: String,//mongoose type 인 String 으로 일반적인 string 과는 겉으로는 대문자 차이
rquired: true, rquired: true,
unique: true, unique: true,
validate: [validateEmail, "이메일을 입력해주세요"], validate: [validateEmail, "이메일을 입력해주세요"],
......
...@@ -9,4 +9,5 @@ router.use("/users", userRouter); ...@@ -9,4 +9,5 @@ router.use("/users", userRouter);
router.use("/auth", authRouter); router.use("/auth", authRouter);
router.use("/posts", postRouter); //router.route router.use("/posts", postRouter); //router.route
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