Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
students
travel
Commits
35f41d6b
Commit
35f41d6b
authored
Jul 25, 2022
by
Lee Soobeom
Browse files
Merge remote-tracking branch 'origin/sb15' into develop
parents
4fec5250
7285bfbd
Changes
18
Hide whitespace changes
Inline
Side-by-side
frontend/src/App.tsx
View file @
35f41d6b
...
@@ -29,7 +29,6 @@ export const App = () => {
...
@@ -29,7 +29,6 @@ export const App = () => {
<
Route
path
=
"board"
element
=
{
<
Board
/>
}
/>
<
Route
path
=
"board"
element
=
{
<
Board
/>
}
/>
<
Route
path
=
"post/:postId"
element
=
{
<
IntoPost
/>
}
/>
<
Route
path
=
"post/:postId"
element
=
{
<
IntoPost
/>
}
/>
<
Route
path
=
"edit"
element
=
{
<
EditPost
/>
}
/>
<
Route
path
=
"edit"
element
=
{
<
EditPost
/>
}
/>
{
/* </Route> */
}
<
Route
<
Route
path
=
"profile"
path
=
"profile"
element
=
{
element
=
{
...
@@ -39,7 +38,8 @@ export const App = () => {
...
@@ -39,7 +38,8 @@ export const App = () => {
}
}
/>
/>
<
Route
path
=
"admin"
element
=
{
<
Admin
/>
}
/>
<
Route
path
=
"admin"
element
=
{
<
Admin
/>
}
/>
<
Route
path
=
"admin/:imgId"
element
=
{
<
ImgRewrite
/>
}
/>
<
Route
path
=
"admin/:imgId"
element
=
{
<
ImgRewrite
/>
}
/>
<
Route
path
=
"rewrite"
element
=
{
<
ImgRewrite
/>
}
/>
</
Route
>
</
Route
>
</
Route
>
</
Route
>
</
Routes
>
</
Routes
>
...
...
frontend/src/apis/post.api.ts
View file @
35f41d6b
...
@@ -2,39 +2,39 @@ import axios from "axios";
...
@@ -2,39 +2,39 @@ import axios from "axios";
import
baseUrl
from
"
./baseUrl
"
;
import
baseUrl
from
"
./baseUrl
"
;
import
{
PostType
}
from
"
../types
"
;
import
{
PostType
}
from
"
../types
"
;
export
const
posting
=
async
(
post
:
PostType
)
=>
{
export
const
createFileAndPost
=
async
(
formdata
:
FormData
)
=>
{
const
{
data
}
=
await
axios
.
post
(
`
${
baseUrl
}
/posts/`
,
post
);
const
{
data
}
=
await
axios
.
post
(
`
${
baseUrl
}
/posts/`
,
formdata
);
return
data
;
return
data
;
};
};
export
const
getData
=
async
()
=>
{
export
const
getData
=
async
()
=>
{
const
{
data
}
=
await
axios
.
get
(
`
${
baseUrl
}
/posts/`
);
const
{
data
}
=
await
axios
.
get
(
`
${
baseUrl
}
/posts/`
);
return
data
;
return
data
;
};
};
//board
export
const
addCounts
=
async
(
_id
:
string
,
counts
:
number
)
=>
{
export
const
getFileByPostId
=
async
(
postId
:
string
)
=>
{
const
{
data
}
=
await
axios
.
post
(
`
${
baseUrl
}
/posts/
${
_id
}
`
,
{
const
{
data
}
=
await
axios
.
get
(
`
${
baseUrl
}
/posts/files/
${
postId
}
`
);
counts
:
counts
+
1
,
});
return
data
;
return
data
;
};
};
//
export
const
get
PostByPostId
=
async
(
_id
:
string
)
=>
{
export
const
get
ImgData
=
async
(
name
:
string
)
=>
{
const
{
data
}
=
await
axios
.
get
(
`
${
baseUrl
}
/posts/
${
_id
}
`
);
const
{
data
}
=
await
axios
.
get
(
`
/images/
${
name
}
`
);
return
data
;
return
data
;
};
};
export
const
deletePost
=
async
(
_id
:
string
)
=>
{
export
const
addCounts
=
async
(
postId
:
string
,
counts
:
number
)
=>
{
const
{
data
}
=
await
axios
.
delete
(
`
${
baseUrl
}
/posts/
${
_id
}
`
);
const
{
data
}
=
await
axios
.
post
(
`
${
baseUrl
}
/posts/
${
postId
}
`
,
{
counts
:
counts
+
1
,
});
return
data
;
return
data
;
};
};
export
const
updating
=
async
(
post
:
PostType
)
=>
{
export
const
deletePost
=
async
(
post
Id
:
string
)
=>
{
const
{
data
}
=
await
axios
.
put
(
`
${
baseUrl
}
/posts/
${
post
.
_id
}
`
,
post
);
const
{
data
}
=
await
axios
.
delete
(
`
${
baseUrl
}
/posts/
${
post
Id
}
`
);
return
data
;
return
data
;
};
};
export
const
postImg
=
async
(
formdata
:
FormData
)
=>
{
export
const
updateImgAndPost
=
async
(
postId
:
string
,
formdata
:
FormData
)
=>
{
const
{
data
}
=
await
axios
.
p
os
t
(
`
${
baseUrl
}
/posts`
,
formdata
);
const
{
data
}
=
await
axios
.
p
u
t
(
`
${
baseUrl
}
/posts
/
${
postId
}
`
,
formdata
);
return
data
;
return
data
;
};
};
frontend/src/board/board.tsx
View file @
35f41d6b
...
@@ -13,8 +13,8 @@ export default function BoardPage() {
...
@@ -13,8 +13,8 @@ export default function BoardPage() {
useEffect
(()
=>
{
useEffect
(()
=>
{
getDataList
();
getDataList
();
},
[
posts
]);
},
[]);
// posts
const
getDataList
=
async
()
=>
{
const
getDataList
=
async
()
=>
{
const
res
=
await
postApi
.
getData
();
const
res
=
await
postApi
.
getData
();
setPosts
(
res
);
setPosts
(
res
);
...
@@ -37,22 +37,22 @@ export default function BoardPage() {
...
@@ -37,22 +37,22 @@ export default function BoardPage() {
return
(
return
(
<
div
className
=
"flex flex-col items-center"
>
<
div
className
=
"flex flex-col items-center"
>
<
div
className
=
"flex flex-col items-center mt-6"
>
<
div
className
=
"flex flex-col
w-10/12
items-center mt-6"
>
<
div
>
`Travel Report's Board`
</
div
>
<
div
>
`Travel Report's Board`
</
div
>
<
div
>
`여행지 후기를 남겨주세요!`
</
div
>
<
div
>
`여행지 후기를 남겨주세요!`
</
div
>
</
div
>
</
div
>
<
div
className
=
"flex flex-col w-10/12 mt-16"
>
<
div
className
=
"flex flex-col w-10/12 mt-16"
>
<
div
className
=
"flex justify-end"
>
<
div
className
=
"flex justify-end"
>
<
div
className
=
"border-2 mb-2"
>
<
div
className
=
"border-2
border-blue-500 rounded
mb-2"
>
<
Link
to
=
"/posting"
>
<
Link
to
=
"/posting"
>
<
button
>
글쓰기
+
</
button
>
<
button
>
글쓰기
</
button
>
</
Link
>
</
Link
>
</
div
>
{
"
"
}
</
div
>
{
"
"
}
{
/* Link */
}
{
/* Link */
}
</
div
>
</
div
>
<
div
className
=
"sm:overflow-y-scroll"
>
<
div
className
=
"sm:overflow-y-scroll"
>
<
div
className
=
"flex flex-row divide-x-2 border-2 border-solid b
g-sky-300 b
order-y-2 h-10 "
>
<
div
className
=
"flex flex-row divide-x-2 border-2 border-solid border-y-2 h-1
0 bg-gradient-to-r from-cyan-500 to-blue-50
0 "
>
<
div
className
=
"basis-full"
>
Title
</
div
>
<
div
className
=
"basis-full"
>
Title
</
div
>
<
div
className
=
"basis-3/12"
>
Date
</
div
>
<
div
className
=
"basis-3/12"
>
Date
</
div
>
<
div
className
=
"basis-2/12"
>
Clicks
</
div
>
<
div
className
=
"basis-2/12"
>
Clicks
</
div
>
...
...
frontend/src/post/editpost.tsx
View file @
35f41d6b
import
React
,
{
FormEvent
,
useState
}
from
"
react
"
;
import
React
,
{
FormEvent
,
useState
,
useEffect
}
from
"
react
"
;
import
{
useNavigate
,
useLocation
}
from
"
react-router-dom
"
;
import
{
useNavigate
,
useLocation
}
from
"
react-router-dom
"
;
import
isLength
from
"
validator/lib/isLength
"
;
import
isLength
from
"
validator/lib/isLength
"
;
import
equals
from
"
validator/lib/equals
"
;
import
equals
from
"
validator/lib/equals
"
;
...
@@ -6,12 +6,16 @@ import { catchErrors } from "../helpers";
...
@@ -6,12 +6,16 @@ import { catchErrors } from "../helpers";
import
{
PostType
}
from
"
../types
"
;
import
{
PostType
}
from
"
../types
"
;
import
{
postApi
}
from
"
../apis
"
;
import
{
postApi
}
from
"
../apis
"
;
import
{
PostState
}
from
"
./intopost
"
;
import
{
PostState
}
from
"
./intopost
"
;
import
{
FileType
}
from
"
./intopost
"
;
export
function
EditPost
()
{
export
function
EditPost
()
{
const
[
city
,
setCity
]
=
useState
<
string
>
(
"
질문종류
"
);
const
[
city
,
setCity
]
=
useState
<
string
>
(
"
city
"
);
const
[
theme
,
setTheme
]
=
useState
<
string
>
(
"
질문종류
"
);
const
[
theme
,
setTheme
]
=
useState
<
string
>
(
"
theme
"
);
const
[
title
,
setTitle
]
=
useState
<
string
>
(
""
);
const
[
title
,
setTitle
]
=
useState
<
string
>
(
""
);
const
[
text
,
setText
]
=
useState
<
string
>
(
""
);
const
[
text
,
setText
]
=
useState
<
string
>
(
""
);
const
[
file
,
setFile
]
=
useState
<
FileList
>
();
const
[
imgSrc
,
setImgSrc
]
=
useState
<
string
[]
>
();
const
[
filesList
,
setFilesList
]
=
useState
<
FileType
[]
>
();
const
navigate
=
useNavigate
();
const
navigate
=
useNavigate
();
const
location
=
useLocation
()
as
PostState
;
const
location
=
useLocation
()
as
PostState
;
...
@@ -24,7 +28,7 @@ export function EditPost() {
...
@@ -24,7 +28,7 @@ export function EditPost() {
city
:
post
.
city
,
city
:
post
.
city
,
date
:
post
.
date
,
date
:
post
.
date
,
user
:
post
.
user
,
user
:
post
.
user
,
counts
:
0
,
counts
:
post
.
counts
,
_id
:
post
.
_id
,
_id
:
post
.
_id
,
});
});
...
@@ -33,21 +37,51 @@ export function EditPost() {
...
@@ -33,21 +37,51 @@ export function EditPost() {
const
[
disabled
,
setDisabled
]
=
useState
(
false
);
const
[
disabled
,
setDisabled
]
=
useState
(
false
);
const
[
success
,
setSuccess
]
=
useState
(
false
);
const
[
success
,
setSuccess
]
=
useState
(
false
);
async
function
reWriteSubmit
(
event
:
FormEvent
)
{
useEffect
(()
=>
{
event
.
preventDefault
();
getFilesList
(
post
.
_id
);
try
{
},
[]);
setError
(
""
);
cons
ole
.
log
(
"
user data
"
,
user
);
cons
t
imgArr
=
new
Array
(
);
if
(
postingFormMatch
(
user
)
===
true
)
{
const
getFilesList
=
async
(
postId
:
string
)
=>
{
setLoading
(
true
);
const
res
=
await
postApi
.
getFileByPostId
(
postId
);
//_id는 req.params에 항상 같이 보낸다
const
res
=
await
postApi
.
updating
(
user
);
setFilesList
(
res
);
};
const
updateImg2Db
=
async
(
filelist
:
FileList
)
=>
{
const
formdata
=
new
FormData
();
formdata
.
append
(
"
title
"
,
user
.
title
);
formdata
.
append
(
"
text
"
,
user
.
text
);
formdata
.
append
(
"
theme
"
,
user
.
theme
);
formdata
.
append
(
"
city
"
,
user
.
city
);
if
(
filelist
===
undefined
||
filelist
===
null
)
{
const
res
=
await
postApi
.
updateImgAndPost
(
user
.
_id
,
formdata
);
}
else
{
for
(
var
i
=
0
;
i
<
filelist
.
length
;
i
++
)
{
formdata
.
append
(
"
picture
"
,
filelist
?.[
i
]);
}
const
res
=
await
postApi
.
updateImgAndPost
(
user
.
_id
,
formdata
);
}
};
console
.
log
(
"
clear
"
,
res
);
async
function
reWriteSubmit
(
event
:
FormEvent
)
{
navigate
(
"
/board
"
,
{
replace
:
true
});
event
.
preventDefault
();
setSuccess
(
true
);
try
{
if
(
confirm
(
"
게시물을 수정하시겠습니까?
"
)
==
true
)
{
setError
(
""
);
setError
(
""
);
// console.log("user data", user);
if
(
postingFormMatch
(
user
)
===
true
)
{
setLoading
(
true
);
if
(
file
)
{
const
res
=
updateImg2Db
(
file
);
// console.log(res);
}
navigate
(
"
/board
"
,
{
replace
:
true
});
setSuccess
(
true
);
setError
(
""
);
}
}
else
{
return
false
;
}
}
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
log
(
"
에러발생
"
);
console
.
log
(
"
에러발생
"
);
...
@@ -57,19 +91,48 @@ export function EditPost() {
...
@@ -57,19 +91,48 @@ export function EditPost() {
}
}
}
}
const
handleInputPic
=
async
(
event
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
{
const
maxImg
=
10
;
const
{
files
}
=
event
.
target
;
if
(
!
(
files
===
null
))
{
setFile
(
files
);
}
if
(
!
(
files
?.
length
===
undefined
))
{
if
(
files
?.
length
<=
maxImg
)
{
for
(
var
i
=
0
;
i
<
files
.
length
;
i
++
)
{
const
reader
=
new
FileReader
();
reader
.
readAsDataURL
(
files
?.[
i
]);
reader
.
onload
=
(
e
)
=>
{
imgArr
.
push
(
e
.
target
?.
result
);
setImgSrc
(
imgArr
);
};
}
}
else
{
alert
(
`사진은 최대
${
maxImg
}
장까지 업로드 가능합니다!`
);
}
}
};
function
postingFormMatch
(
user
:
PostType
)
{
function
postingFormMatch
(
user
:
PostType
)
{
if
(
!
isLength
(
user
.
title
??
""
,
{
min
:
1
}))
{
if
(
!
isLength
(
user
.
title
??
""
,
{
min
:
1
}))
{
setError
(
"
제목을 입력해 주세요.
"
);
setError
(
"
제목을 입력해 주세요.
"
);
alert
(
"
제목을 입력해 주세요.
"
);
return
false
;
return
false
;
}
else
if
(
!
isLength
(
user
.
text
??
""
,
{
min
:
1
}))
{
}
else
if
(
!
isLength
(
user
.
text
??
""
,
{
min
:
1
}))
{
alert
(
"
내용을 입력해 주세요.
"
);
setError
(
"
내용을 입력해 주세요.
"
);
setError
(
"
내용을 입력해 주세요.
"
);
return
false
;
return
false
;
}
else
if
(
equals
(
city
,
"
city
"
))
{
}
else
if
(
equals
(
user
.
city
,
"
city
"
))
{
setError
(
"
테마를 선택해 주세요.
"
);
alert
(
"
도시를 선택해 주세요.
"
);
return
false
;
}
else
if
(
equals
(
theme
,
"
theme
"
))
{
setError
(
"
도시를 선택해 주세요.
"
);
setError
(
"
도시를 선택해 주세요.
"
);
return
false
;
return
false
;
}
else
if
(
equals
(
user
.
theme
,
"
theme
"
))
{
alert
(
"
테마를 선택해 주세요.
"
);
setError
(
"
테마를 선택해 주세요.
"
);
return
false
;
}
else
{
}
else
{
return
true
;
return
true
;
}
}
...
@@ -108,75 +171,104 @@ export function EditPost() {
...
@@ -108,75 +171,104 @@ export function EditPost() {
};
};
return
(
return
(
<
div
className
=
"flex flex-col border-3"
>
<
div
className
=
"flex place-content-center"
>
<
form
onSubmit
=
{
reWriteSubmit
}
className
=
"w-full items-center"
>
<
form
<
div
className
=
"flex flex-row relative"
>
onSubmit
=
{
reWriteSubmit
}
<
p
className
=
"basis-1/12 gap-x-8"
>
Id
</
p
>
className
=
"flex flex-col w-96 items-center"
<
p
className
=
"basis-8/12 invisible"
>
empty
</
p
>
>
<
select
<
div
className
=
"flex flex-row h-8 gap-x-1"
>
name
=
"city"
<
div
className
=
"flex border-2 border-sky-400 rounded-full w-20 place-content-center transition ease-in-out delay-150 bg-white-400 hover:-translate-y-1 hover:scale-110 hover:bg-gray-300 duration-300"
>
className
=
"border border-3 border-black w-1/12"
<
input
onChange
=
{
cityChange
}
id
=
"files"
defaultValue
=
{
post
.
city
}
type
=
"file"
>
multiple
<
option
value
=
"city"
>
도시
</
option
>
onChange
=
{
handleInputPic
}
<
option
value
=
"Seoul"
>
서울
</
option
>
className
=
"hidden"
<
option
value
=
"Busan"
>
부산
</
option
>
/>
<
option
value
=
"Incheon"
>
인천
</
option
>
<
label
htmlFor
=
"files"
className
=
"text-xs mt-1.5 ml-1 "
>
<
option
value
=
"Daegoo"
>
대구
</
option
>
파일 선택
<
option
value
=
"Kwangjoo"
>
광주
</
option
>
</
label
>
<
option
value
=
"Daejeon"
>
대전
</
option
>
</
div
>
<
option
value
=
"Woolsan"
>
울산
</
option
>
<
option
value
=
"Sejong"
>
세종
</
option
>
<
div
className
=
"flex w-20"
>
<
option
value
=
"Dokdo"
>
독도
</
option
>
<
select
<
option
value
=
"Jeju"
>
제주
</
option
>
name
=
"city"
</
select
>
className
=
"border-2 border-sky-400 rounded-full w-20 text-xs"
<
select
onChange
=
{
cityChange
}
name
=
"theme"
defaultValue
=
{
post
.
city
}
className
=
"border border-3 border-black w-1/12"
>
onChange
=
{
themeChange
}
<
option
value
=
"city"
>
도시
</
option
>
defaultValue
=
{
post
.
theme
}
<
option
value
=
"Seoul"
>
서울
</
option
>
>
<
option
value
=
"Busan"
>
부산
</
option
>
<
option
value
=
"theme"
>
테마
</
option
>
<
option
value
=
"Incheon"
>
인천
</
option
>
<
option
value
=
"cycling"
>
사이클링
</
option
>
<
option
value
=
"Daegu"
>
대구
</
option
>
<
option
value
=
"surfing"
>
서핑
</
option
>
<
option
value
=
"Gwangju"
>
광주
</
option
>
<
option
value
=
"activity"
>
액티비티
</
option
>
<
option
value
=
"Daejeon"
>
대전
</
option
>
<
option
value
=
"camping"
>
캠핑
</
option
>
<
option
value
=
"Woolsan"
>
울산
</
option
>
<
option
value
=
"sking"
>
스키
</
option
>
<
option
value
=
"Sejong"
>
세종
</
option
>
<
option
value
=
"boat"
>
보트
</
option
>
<
option
value
=
"Dokdo"
>
독도
</
option
>
<
option
value
=
"desert"
>
사막
</
option
>
<
option
value
=
"Jeju"
>
제주
</
option
>
<
option
value
=
"golf"
>
골프
</
option
>
</
select
>
<
option
value
=
"cave"
>
동굴
</
option
>
</
div
>
<
option
value
=
"history"
>
문화재
</
option
>
<
option
value
=
"zoo"
>
동물원
</
option
>
<
div
className
=
"flex w-20"
>
<
option
value
=
"cycling"
>
사이클링
</
option
>
<
select
</
select
>
name
=
"theme"
className
=
"border-2 border-sky-400 rounded-full w-20 text-xs"
<
button
onChange
=
{
themeChange
}
type
=
"submit"
defaultValue
=
{
post
.
theme
}
className
=
"border border-black basis-1/12 gap-x-8"
>
>
<
option
value
=
"theme"
>
테마
</
option
>
Rewrite
<
option
value
=
"cycling"
>
사이클링
</
option
>
</
button
>
<
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
>
</
select
>
</
div
>
<
div
className
=
"flex w-20"
>
<
button
type
=
"submit"
className
=
"border-2 border-sky-400 rounded-full w-20 text-xs text-center transition delay-150 bg-white-400 hover:-translate-y-1 hover:scale-110 hover:bg-sky-300 duration-300"
>
수정
</
button
>
</
div
>
</
div
>
</
div
>
<
div
className
=
"flex
border-4
"
>
<
div
className
=
"flex
flex-col
"
>
<
textarea
<
textarea
name
=
"title"
name
=
"title"
onChange
=
{
titleChange
}
onChange
=
{
titleChange
}
placeholder
=
"title"
placeholder
=
"제목을 입력해 주세요!"
className
=
"w-full h-8"
className
=
"flex w-96 border-2 border-sky-500 rounded"
defaultValue
=
{
post
.
title
}
/>
></
textarea
>
<
div
className
=
"flex flex-col mt-1 mb-1"
>
</
div
>
<
div
className
=
"flex gap-x-2 h-48 overflow-x-scroll"
>
<
div
className
=
"flex border-2"
>
{
filesList
?.
map
((
file
,
i
)
=>
(
<
img
key
=
{
i
}
src
=
{
"
http://localhost:3000/images/
"
+
file
.
newfilename
}
width
=
{
200
}
height
=
{
200
}
/>
))
}
</
div
>
</
div
>
<
textarea
<
textarea
onChange
=
{
textChange
}
onChange
=
{
textChange
}
name
=
"text"
name
=
"text"
placeholder
=
"text"
placeholder
=
"여행 후기를 알려주세요!"
className
=
"w-full h-96"
className
=
"flex w-96 h-96 border-2 border-sky-500 rounded"
defaultValue
=
{
post
.
text
}
/>
></
textarea
>
</
div
>
</
div
>
</
form
>
</
form
>
</
div
>
</
div
>
...
...
frontend/src/post/intopost.tsx
View file @
35f41d6b
import
React
,
{
MouseEvent
}
from
"
react
"
;
import
React
,
{
MouseEvent
,
useEffect
,
useState
}
from
"
react
"
;
import
{
useLocation
,
useNavigate
,
Link
,
Outlet
}
from
"
react-router-dom
"
;
import
{
useLocation
,
useNavigate
,
Link
,
Outlet
}
from
"
react-router-dom
"
;
import
{
catchErrors
}
from
"
../helpers
"
;
import
{
postApi
}
from
"
../apis
"
;
import
{
postApi
}
from
"
../apis
"
;
import
{
PostType
}
from
"
../types
"
;
import
{
PostType
}
from
"
../types
"
;
...
@@ -7,59 +8,118 @@ export interface PostState {
...
@@ -7,59 +8,118 @@ export interface PostState {
state
:
PostType
;
state
:
PostType
;
}
}
export
interface
FileType
{
id
:
string
;
post
:
string
;
originalfilename
:
string
;
newfilename
:
string
;
picturepath
:
string
;
}
export
interface
FilesList
{
filesList
:
FileType
[];
}
export
function
IntoPost
()
{
export
function
IntoPost
()
{
const
location
=
useLocation
()
as
PostState
;
const
location
=
useLocation
()
as
PostState
;
const
post
=
location
.
state
;
const
post
=
location
.
state
;
const
navigate
=
useNavigate
();
const
navigate
=
useNavigate
();
const
[
filesList
,
setFilesList
]
=
useState
<
FileType
[]
>
();
// console.log(post);
// console.log(post);
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
[
error
,
setError
]
=
useState
(
""
);
const
[
addSuccess
,
setAddSuccess
]
=
useState
(
false
);
const
[
delSuccess
,
setDelSuccess
]
=
useState
(
false
);
useEffect
(()
=>
{
getFilesList
(
post
.
_id
);
console
.
log
(
"
newfilename
"
,
filesList
?.[
0
].
newfilename
);
},
[]);
const
getFilesList
=
async
(
postId
:
string
)
=>
{
const
res
=
await
postApi
.
getFileByPostId
(
postId
);
setFilesList
(
res
);
};
const
handleDeleteClick
=
async
(
event
:
MouseEvent
<
HTMLButtonElement
>
)
=>
{
const
handleDeleteClick
=
async
(
event
:
MouseEvent
<
HTMLButtonElement
>
)
=>
{
const
postId
=
event
.
currentTarget
.
id
;
try
{
const
res
=
await
postApi
.
deletePost
(
postId
);
if
(
confirm
(
"
삭제하시겠습니까?
"
)
==
true
)
{
navigate
(
"
/board
"
,
{
replace
:
true
});
const
postId
=
event
.
currentTarget
.
id
;
console
.
log
(
"
delete post
"
,
res
);
const
res
=
await
postApi
.
deletePost
(
postId
);
navigate
(
"
/board
"
,
{
replace
:
true
});
console
.
log
(
"
delete post
"
,
res
);
}
else
{
return
false
;
}
}
catch
(
error
)
{
console
.
log
(
"
에러발생
"
);
catchErrors
(
error
,
setError
);
}
};
};
return
(
return
(
<
div
>
<
div
>
<
div
>
<
div
>
<
div
>
<
div
>
<
div
className
=
"flex flex-row basis-8 gap-x-1"
>
<
div
className
=
"flex flex-row h-8 gap-x-1 place-content-end"
>
<
div
className
=
"border-2 border-sky-300 border-current rounded"
>
<
div
className
=
"w-8"
>
<
button
id
=
{
post
.
_id
}
onClick
=
{
handleDeleteClick
}
>
<
button
id
=
{
post
.
_id
}
onClick
=
{
handleDeleteClick
}
className
=
"border-2 border-sky-100 rounded-full h-8 w-8 text-xs text-center transition delay-150 bg-white-400 hover:-translate-y-1 hover:scale-110 hover:bg-red-300 duration-300"
>
삭제
삭제
</
button
>
</
button
>
</
div
>
</
div
>
<
div
className
=
"
border-2 border-sky-300 border-current rounded
"
>
<
div
className
=
"
w-8
"
>
<
Link
to
=
"/edit"
state
=
{
post
}
>
<
Link
to
=
"/edit"
state
=
{
post
}
>
<
button
>
수정
</
button
>
<
button
className
=
"border-2 border-sky-100 rounded-full h-8 w-8 text-xs transition delay-150 bg-white-400 hover:-translate-y-1 hover:scale-110 hover:bg-sky-300 duration-300"
>
수정
</
button
>
</
Link
>
</
Link
>
</
div
>
</
div
>
</
div
>
</
div
>
<
div
className
=
"flex flex-row gap-x-1"
>
<
div
className
=
"flex flex-col h-16 md:h-8 md:flex-row"
>
<
div
className
=
"flex basis-3/4 border-2 border-sky-300 rounded"
>
<
div
className
=
"flex basis-1/2 place-content-between h-8"
>
제목:
{
post
.
title
}
<
div
className
=
"flex basis-1/2 border-2 border-sky-200 rounded h-8"
>
작성자:
{
post
.
user
.
slice
(
0
,
8
)
}
</
div
>
<
div
className
=
"flex basis-1/2 border-2 border-sky-200 rounded h-8"
>
작성일:
{
post
.
date
.
slice
(
0
,
10
)
}
</
div
>
</
div
>
</
div
>
<
div
className
=
"flex basis-1/4 border-2 border-sky-300 rounded"
>
<
div
className
=
"flex basis-1/2 place-content-between h-8"
>
작성자:
{
post
.
user
}
<
div
className
=
"flex basis-1/3 border-2 border-sky-300 rounded"
>
도시:
{
post
.
city
}
</
div
>
<
div
className
=
"flex basis-1/3 border-2 border-sky-300 rounded"
>
테마:
{
post
.
theme
}
</
div
>
<
div
className
=
"flex basis-1/3 border-2 border-sky-300 rounded"
>
조회수:
{
post
.
counts
}
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
<
div
className
=
"flex flex-row gap-x-1"
>
<
div
className
=
"flex flex-row h-8 gap-x-1 "
>
<
div
className
=
"flex basis-1/4 border-2 border-sky-300 rounded"
>
<
div
className
=
"flex w-full border-2 border-sky-200 rounded"
>
도시:
{
post
.
city
}
제목:
{
post
.
title
}
</
div
>
<
div
className
=
"flex basis-1/4 border-2 border-sky-300 rounded"
>
테마:
{
post
.
theme
}
</
div
>
<
div
className
=
"flex basis-1/4 border-2 border-sky-300 rounded"
>
작성일:
{
post
.
date
}
</
div
>
<
div
className
=
"flex basis-1/4 border-2 border-sky-300 rounded"
>
조회수:
{
post
.
counts
}
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
<
div
className
=
"border-2 border-sky-300 rounded h-96"
>
{
post
.
text
}
</
div
>
<
div
className
=
"flex-row border-2 border-sky-400 rounded h-48 gap-x-2 "
>
<
div
className
=
"flex gap-x-2 h-48 overflow-x-scroll"
>
{
filesList
?.
map
((
file
,
i
)
=>
(
<
img
key
=
{
i
}
src
=
{
"
http://localhost:3000/images/
"
+
file
.
newfilename
}
width
=
{
200
}
height
=
{
200
}
/>
))
}
</
div
>
</
div
>
<
div
className
=
"border-2 border-sky-500 rounded h-96"
>
{
post
.
text
}
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
);
);
...
...
frontend/src/post/post.tsx
View file @
35f41d6b
...
@@ -18,7 +18,7 @@ export default function Post({ handleClick, post }: Props) {
...
@@ -18,7 +18,7 @@ export default function Post({ handleClick, post }: Props) {
</
button
>
</
button
>
</
Link
>
</
Link
>
</
div
>
</
div
>
<
div
className
=
"basis-3/12"
>
{
post
.
date
}
</
div
>
<
div
className
=
"basis-3/12"
>
{
post
.
date
.
slice
(
0
,
10
)
}
</
div
>
<
div
className
=
"basis-2/12"
>
{
post
.
counts
}
</
div
>
<
div
className
=
"basis-2/12"
>
{
post
.
counts
}
</
div
>
</
div
>
</
div
>
);
);
...
...
frontend/src/post/posting.tsx
View file @
35f41d6b
...
@@ -5,14 +5,13 @@ import equals from "validator/lib/equals";
...
@@ -5,14 +5,13 @@ import equals from "validator/lib/equals";
import
{
catchErrors
}
from
"
../helpers
"
;
import
{
catchErrors
}
from
"
../helpers
"
;
import
{
PostType
}
from
"
../types
"
;
import
{
PostType
}
from
"
../types
"
;
import
{
postApi
}
from
"
../apis
"
;
import
{
postApi
}
from
"
../apis
"
;
import
{
File
}
from
"
formidable
"
;
export
default
function
Posting
()
{
export
default
function
Posting
()
{
const
[
city
,
setCity
]
=
useState
<
string
>
(
"
city
"
);
const
[
city
,
setCity
]
=
useState
<
string
>
(
"
city
"
);
const
[
theme
,
setTheme
]
=
useState
<
string
>
(
"
theme
"
);
const
[
theme
,
setTheme
]
=
useState
<
string
>
(
"
theme
"
);
const
[
title
,
setTitle
]
=
useState
<
string
>
(
""
);
const
[
title
,
setTitle
]
=
useState
<
string
>
(
""
);
const
[
text
,
setText
]
=
useState
<
string
>
(
""
);
const
[
text
,
setText
]
=
useState
<
string
>
(
""
);
const
[
file
,
setFile
]
=
useState
<
File
>
();
const
[
file
,
setFile
]
=
useState
<
File
List
>
();
const
[
imgSrc
,
setImgSrc
]
=
useState
<
string
[]
>
();
const
[
imgSrc
,
setImgSrc
]
=
useState
<
string
[]
>
();
const
navigate
=
useNavigate
();
const
navigate
=
useNavigate
();
...
@@ -32,38 +31,48 @@ export default function Posting() {
...
@@ -32,38 +31,48 @@ export default function Posting() {
const
[
disabled
,
setDisabled
]
=
useState
(
false
);
const
[
disabled
,
setDisabled
]
=
useState
(
false
);
const
[
success
,
setSuccess
]
=
useState
(
false
);
const
[
success
,
setSuccess
]
=
useState
(
false
);
useEffect
(()
=>
{
console
.
log
(
"
uploaded imgs
"
,
imgSrc
);
},
[
imgSrc
]);
const
imgArr
=
new
Array
();
const
imgArr
=
new
Array
();
const
sendImg2Db
=
async
(
filelist
:
FileList
)
=>
{
const
sendImg2Db
=
async
(
filelist
:
FileList
)
=>
{
const
formdata
=
new
FormData
();
const
formdata
=
new
FormData
();
formdata
.
append
(
"
title
"
,
user
.
title
);
formdata
.
append
(
"
text
"
,
user
.
text
);
formdata
.
append
(
"
theme
"
,
user
.
theme
);
formdata
.
append
(
"
city
"
,
user
.
city
);
if
(
!
(
filelist
===
undefined
||
filelist
===
null
))
{
if
(
!
(
filelist
===
undefined
||
filelist
===
null
))
{
for
(
var
i
=
0
;
i
<
filelist
.
length
;
i
++
)
{
if
(
filelist
.
length
===
1
)
{
formdata
.
append
(
`picture
${
i
}
`
,
filelist
?.[
i
]);
formdata
.
append
(
"
picture
"
,
filelist
?.[
0
]);
const
res
=
await
postApi
.
createFileAndPost
(
formdata
);
}
else
{
for
(
var
i
=
0
;
i
<
filelist
.
length
;
i
++
)
{
formdata
.
append
(
"
picture
"
,
filelist
?.[
i
]);
}
const
res
=
await
postApi
.
createFileAndPost
(
formdata
);
}
}
console
.
log
(
"
formdata
"
,
formdata
);
await
postApi
.
postImg
(
formdata
);
}
}
};
};
async
function
handlePostSubmit
(
event
:
FormEvent
)
{
async
function
handlePostSubmit
(
event
:
FormEvent
)
{
event
.
preventDefault
();
event
.
preventDefault
();
try
{
try
{
setError
(
""
);
if
(
confirm
(
"
게시물을 작성하시겠습니까?
"
)
==
true
)
{
console
.
log
(
"
user data
"
,
user
);
if
(
postingFormMatch
())
{
setLoading
(
true
);
// sendImg2Db();
const
res
=
await
postApi
.
posting
(
user
);
console
.
log
(
"
서버연결됬나요
"
,
res
);
// console.log("user save");
navigate
(
"
/board
"
,
{
replace
:
true
});
setSuccess
(
true
);
setError
(
""
);
setError
(
""
);
// console.log("user data", user);
if
(
!
(
imgSrc
===
undefined
))
{
if
(
postingFormMatch
(
user
,
imgSrc
))
{
setLoading
(
true
);
if
(
file
)
{
const
res
=
sendImg2Db
(
file
);
// console.log(res);
}
}
navigate
(
"
/board
"
,
{
replace
:
true
});
setSuccess
(
true
);
setError
(
""
);
}
}
else
{
return
false
;
}
}
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
log
(
"
에러발생
"
);
console
.
log
(
"
에러발생
"
);
...
@@ -73,18 +82,26 @@ export default function Posting() {
...
@@ -73,18 +82,26 @@ export default function Posting() {
}
}
}
}
function
postingFormMatch
()
{
function
postingFormMatch
(
user
:
PostType
,
imgSrc
:
string
[]
)
{
if
(
!
isLength
(
user
.
title
??
""
,
{
min
:
1
}))
{
if
(
!
isLength
(
user
.
title
??
""
,
{
min
:
1
}))
{
alert
(
"
제목을 입력해 주세요.
"
);
setError
(
"
제목을 입력해 주세요.
"
);
setError
(
"
제목을 입력해 주세요.
"
);
return
false
;
return
false
;
}
else
if
(
!
isLength
(
user
.
text
??
""
,
{
min
:
1
}))
{
}
else
if
(
!
isLength
(
user
.
text
??
""
,
{
min
:
1
}))
{
alert
(
"
내용을 입력해 주세요.
"
);
setError
(
"
내용을 입력해 주세요.
"
);
setError
(
"
내용을 입력해 주세요.
"
);
return
false
;
return
false
;
}
else
if
(
equals
(
city
,
"
질문종류
"
))
{
}
else
if
(
equals
(
city
,
"
city
"
))
{
alert
(
"
도시를 선택해 주세요.
"
);
setError
(
"
도시를 선택해 주세요.
"
);
return
false
;
}
else
if
(
equals
(
theme
,
"
theme
"
))
{
alert
(
"
테마를 선택해 주세요.
"
);
setError
(
"
테마를 선택해 주세요.
"
);
setError
(
"
테마를 선택해 주세요.
"
);
return
false
;
return
false
;
}
else
if
(
equals
(
theme
,
"
질문종류
"
))
{
}
else
if
(
imgSrc
===
undefined
||
imgSrc
===
null
)
{
setError
(
"
도시를 선택해 주세요.
"
);
alert
(
"
사진을 첨부해 주세요.
"
);
setError
(
"
사진을 첨부해 주세요.
"
);
return
false
;
return
false
;
}
else
{
}
else
{
return
true
;
return
true
;
...
@@ -124,10 +141,13 @@ export default function Posting() {
...
@@ -124,10 +141,13 @@ export default function Posting() {
};
};
const
handleInputPic
=
async
(
event
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
{
const
handleInputPic
=
async
(
event
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
{
event
.
preventDefault
();
const
maxImg
=
10
;
const
maxImg
=
10
;
const
{
files
}
=
event
.
target
;
const
{
files
}
=
event
.
target
;
if
(
!
(
files
===
null
))
{
setFile
(
files
);
}
if
(
!
(
files
?.
length
===
undefined
))
{
if
(
!
(
files
?.
length
===
undefined
))
{
if
(
files
?.
length
<=
maxImg
)
{
if
(
files
?.
length
<=
maxImg
)
{
for
(
var
i
=
0
;
i
<
files
.
length
;
i
++
)
{
for
(
var
i
=
0
;
i
<
files
.
length
;
i
++
)
{
...
@@ -140,89 +160,105 @@ export default function Posting() {
...
@@ -140,89 +160,105 @@ export default function Posting() {
};
};
}
}
}
else
{
}
else
{
alert
(
"
사진은 최대
10
장까지 업로드 가능합니다!
"
);
alert
(
`
사진은 최대
${
maxImg
}
장까지 업로드 가능합니다!
`
);
}
}
}
}
};
};
return
(
return
(
<
div
className
=
"flex flex-col border-3 "
>
<
div
className
=
"flex place-content-center"
>
<
form
onSubmit
=
{
handlePostSubmit
}
className
=
"w-full items-center"
>
<
form
<
div
className
=
"flex flex-row relative"
>
onSubmit
=
{
handlePostSubmit
}
<
p
className
=
"basis-1/12 gap-x-8"
>
Id
</
p
>
className
=
"flex flex-col w-96 items-center"
<
p
className
=
"basis-6/12 invisible"
>
empty
</
p
>
>
<
div
className
=
"basis-2/12 border-2 border-sky-300"
>
<
div
className
=
"flex flex-row h-8 gap-x-1"
>
<
input
type
=
"file"
multiple
onChange
=
{
handleInputPic
}
/>
<
div
className
=
"flex border-2 border-sky-400 rounded-full w-20 place-content-center transition delay-150 bg-white-400 hover:-translate-y-1 hover:scale-110 hover:bg-gray-300 duration-300"
>
<
input
id
=
"files"
type
=
"file"
multiple
onChange
=
{
handleInputPic
}
className
=
"hidden"
/>
<
label
htmlFor
=
"files"
className
=
"text-xs text-center mt-1.5 ml-1 "
>
파일 선택
</
label
>
</
div
>
<
div
className
=
"flex w-20"
>
<
select
name
=
"city"
className
=
"border-2 border-sky-400 rounded-full w-20 text-xs"
onChange
=
{
cityChange
}
defaultValue
=
"질문종류"
>
<
option
value
=
"질문종류"
>
도시
</
option
>
<
option
value
=
"Seoul"
>
서울
</
option
>
<
option
value
=
"Busan"
>
부산
</
option
>
<
option
value
=
"Incheon"
>
인천
</
option
>
<
option
value
=
"Daegu"
>
대구
</
option
>
<
option
value
=
"Gwangju"
>
광주
</
option
>
<
option
value
=
"Daejeon"
>
대전
</
option
>
<
option
value
=
"Woolsan"
>
울산
</
option
>
<
option
value
=
"Sejong"
>
세종
</
option
>
<
option
value
=
"Dokdo"
>
독도
</
option
>
<
option
value
=
"Jeju"
>
제주
</
option
>
</
select
>
</
div
>
<
div
className
=
"flex w-20"
>
<
select
name
=
"theme"
className
=
"border-2 border-sky-400 rounded-full w-20 text-xs"
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
>
</
select
>
</
div
>
<
div
className
=
"flex w-20"
>
<
button
type
=
"submit"
className
=
"border-2 border-sky-400 rounded-full w-20 text-xs text-center transition ease-in-out delay-150 bg-white-400 hover:-translate-y-1 hover:scale-110 hover:bg-sky-300 duration-300"
>
글쓰기
</
button
>
</
div
>
</
div
>
<
select
name
=
"city"
id
=
"Questions"
className
=
"border-2 border-sky-300 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
>
<
select
name
=
"theme"
id
=
"Questions"
className
=
"border-2 border-sky-300 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
>
</
select
>
<
button
type
=
"submit"
className
=
"border-2 border-sky-300 basis-1/12 gap-x-8"
>
글쓰기
</
button
>
</
div
>
</
div
>
<
div
className
=
"flex"
>
<
div
className
=
"flex
flex-col
"
>
<
textarea
<
textarea
name
=
"title"
name
=
"title"
onChange
=
{
titleChange
}
onChange
=
{
titleChange
}
placeholder
=
"
title
"
placeholder
=
"
제목을 입력해 주세요!
"
className
=
"
w-full h-8
border-2 border-sky-
3
00"
className
=
"
flex w-96
border-2 border-sky-
5
00
rounded
"
></
textarea
>
/
>
</
div
>
<
div
className
=
"flex flex-col mt-1 mb-1"
>
<
div
className
=
"flex
flex-co
l"
>
<
div
className
=
"flex
gap-x-2 h-48 overflow-x-scrol
l"
>
<
div
className
=
"flex h-48 overflow-x-scroll"
>
{
imgSrc
?.
map
((
img
,
i
)
=>
(
{
imgSrc
?.
map
((
img
,
i
)
=>
(
<
img
key
=
{
i
}
src
=
{
img
}
width
=
{
200
}
height
=
{
200
}
/>
<
img
key
=
{
i
}
src
=
{
img
}
width
=
{
200
}
height
=
{
200
}
/>
))
}
))
}
</
div
>
</
div
>
</
div
>
<
textarea
<
textarea
onChange
=
{
textChange
}
onChange
=
{
textChange
}
name
=
"text"
name
=
"text"
placeholder
=
"
text
"
placeholder
=
"
여행 후기를 알려주세요!
"
className
=
"
w-full
h-96 border-2 border-sky-
3
00"
className
=
"
flex w-96
h-96 border-2 border-sky-
5
00
rounded
"
></
textarea
>
/
>
</
div
>
</
div
>
</
form
>
</
form
>
</
div
>
</
div
>
...
...
frontend/src/types/index.tsx
View file @
35f41d6b
...
@@ -16,7 +16,7 @@ export interface PostType {
...
@@ -16,7 +16,7 @@ export interface PostType {
text
:
string
;
text
:
string
;
theme
:
string
;
theme
:
string
;
city
:
string
;
city
:
string
;
date
:
string
|
number
;
date
:
string
;
counts
:
number
;
counts
:
number
;
_id
:
string
;
_id
:
string
;
user
:
string
;
user
:
string
;
...
...
src/controllers/fileinfo.controller.ts
0 → 100644
View file @
35f41d6b
import
formidable
from
"
formidable
"
;
import
{
asyncWrap
}
from
"
../helpers/asyncWrap
"
;
import
{
TypedRequest
}
from
"
../types
"
;
export
const
uploadFile
=
asyncWrap
(
async
(
reqExp
,
res
,
next
)
=>
{
const
req
=
reqExp
as
TypedRequest
;
const
form
=
formidable
({
multiples
:
false
,
uploadDir
:
"
uploads
"
});
await
new
Promise
((
resolve
,
reject
)
=>
{
form
.
parse
(
req
,
(
err
,
fields
,
files
)
=>
{
if
(
err
)
{
reject
(
err
);
return
;
}
console
.
log
(
"
fields
"
,
fields
);
console
.
log
(
"
files
"
,
files
);
req
.
body
=
fields
;
req
.
files
=
files
;
resolve
(
files
);
});
});
next
();
return
;
});
export
const
uploadFiles
=
asyncWrap
(
async
(
reqExp
,
res
,
next
)
=>
{
const
req
=
reqExp
as
TypedRequest
;
const
form
=
formidable
({
multiples
:
true
,
uploadDir
:
"
uploads
"
});
await
new
Promise
((
resolve
,
reject
)
=>
{
form
.
parse
(
req
,
(
err
,
fields
,
files
)
=>
{
if
(
err
)
{
reject
(
err
);
return
;
}
console
.
log
(
"
fields
"
,
fields
);
console
.
log
(
"
files
"
,
files
);
req
.
body
=
fields
;
req
.
files
=
files
;
resolve
(
files
);
});
});
next
();
return
;
});
src/controllers/index.ts
View file @
35f41d6b
export
*
as
authCtrl
from
"
./auth.controller
"
;
export
*
as
authCtrl
from
"
./auth.controller
"
;
export
*
as
fileInfoCtrl
from
"
./fileinfo.controller
"
;
export
*
as
mainimgCtrl
from
"
./mainimg.controller
"
;
export
*
as
postCtrl
from
"
./post.controller
"
;
export
*
as
postCtrl
from
"
./post.controller
"
;
export
*
as
roleCtrl
from
"
./role.controller
"
;
export
*
as
roleCtrl
from
"
./role.controller
"
;
export
*
as
userCtrl
from
"
./user.controller
"
;
export
*
as
userCtrl
from
"
./user.controller
"
;
export
*
as
mainimgCtrl
from
"
./mainimg.controller
"
;
\ No newline at end of file
src/controllers/post.controller.ts
View file @
35f41d6b
import
{
NextFunction
,
Request
,
Response
}
from
"
express
"
;
import
{
NextFunction
,
Request
,
Response
}
from
"
express
"
;
import
isLength
from
"
validator/lib/isLength
"
;
import
formidable
,
{
Fields
,
Files
}
from
"
formidable
"
;
import
equals
from
"
validator/lib/equals
"
;
import
{
TypedRequestAuth
}
from
"
./auth.controller
"
;
import
{
TypedRequestAuth
}
from
"
./auth.controller
"
;
import
{
asyncWrap
}
from
"
../helpers
"
;
import
{
asyncWrap
}
from
"
../helpers
"
;
import
{
postDb
,
userDb
}
from
"
../db
"
;
import
{
postDb
,
userDb
}
from
"
../db
"
;
import
{
TypedRequest
}
from
"
../types
"
;
import
{
TypedRequest
}
from
"
../types
"
;
export
const
postCreate
=
asyncWrap
(
async
(
reqExp
,
res
,
next
)
=>
{
export
const
createFileAndPost
=
asyncWrap
(
async
(
reqExp
,
res
,
next
)
=>
{
const
req
=
reqExp
as
TypedRequestAuth
<
{
userId
:
string
}
>
;
const
req
=
reqExp
as
TypedRequestAuth
<
{
userId
:
string
}
>
;
const
{
title
,
text
,
theme
,
city
,
date
}
=
req
.
body
as
{
const
{
userId
}
=
req
.
auth
;
title
:
string
;
text
:
string
;
theme
:
string
;
city
:
string
;
date
:
Date
;
counts
:
number
;
};
// 1) title 빈 문자열인지 확인
if
(
!
isLength
(
title
??
""
,
{
min
:
1
}))
{
return
res
.
status
(
422
).
send
(
"
제목을 한 글자 이상 입력해주세요
"
);
}
// 2) body 빈 문자열인지 확인
if
(
!
isLength
(
text
??
""
,
{
min
:
1
}))
{
return
res
.
status
(
422
).
send
(
"
제목을 한 글자 이상 입력해주세요
"
);
}
// 3) theme dropdown default-value "테마"일 경우 에러
const
form
=
formidable
({
if
(
equals
(
theme
,
"
질문종류
"
))
{
uploadDir
:
"
uploads
"
,
return
res
.
status
(
422
).
send
(
"
테마를 입력해 주세요
"
);
keepExtensions
:
true
,
}
multiples
:
true
,
// 4) city dropdown default-value "도시"일 경우 에러
if
(
equals
(
city
,
"
질문종류
"
))
{
return
res
.
status
(
422
).
send
(
"
도시를 선택해 주세요
"
);
}
const
userId
=
req
.
auth
.
userId
;
const
newPost
=
await
postDb
.
createPost
({
title
,
text
,
theme
,
city
,
date
:
Date
.
now
(),
user
:
userId
,
});
});
console
.
log
(
"
post
"
,
newPost
);
const
fileIdArr
=
new
Array
();
return
res
.
json
(
newPost
);
form
.
parse
(
req
,
async
(
err
,
fields
,
files
)
=>
{
if
(
!
Array
.
isArray
(
fields
.
title
))
{
const
title
=
fields
.
title
;
if
(
!
Array
.
isArray
(
fields
.
text
))
{
const
text
=
fields
.
text
;
if
(
!
Array
.
isArray
(
fields
.
theme
))
{
const
theme
=
fields
.
theme
;
if
(
!
Array
.
isArray
(
fields
.
city
))
{
const
city
=
fields
.
city
;
if
(
Array
.
isArray
(
files
.
picture
))
{
for
(
var
i
=
0
;
i
<
files
.
picture
.
length
;
i
++
)
{
const
originalfilename
=
files
.
picture
?.[
i
].
originalFilename
;
const
newfilename
=
files
.
picture
?.[
i
].
newFilename
;
const
filepath
=
files
.
picture
?.[
i
].
filepath
;
const
filesRes
=
await
postDb
.
createFilesRow
(
originalfilename
,
newfilename
,
filepath
);
fileIdArr
.
push
(
filesRes
);
}
}
else
if
(
!
Array
.
isArray
(
files
.
picture
))
{
const
originalfilename
=
files
.
picture
.
originalFilename
;
const
newfilename
=
files
.
picture
.
newFilename
;
const
filepath
=
files
.
picture
.
filepath
;
const
filesRes
=
await
postDb
.
createFilesRow
(
originalfilename
,
newfilename
,
filepath
);
fileIdArr
.
push
(
filesRes
);
}
// file or one or more
const
postRes
=
await
postDb
.
createPostRow
({
title
,
text
,
theme
,
city
,
date
:
Date
.
now
(),
counts
:
0
,
user
:
userId
,
file
:
fileIdArr
,
});
}
}
}
}
});
});
});
export
const
getAllPost
=
asyncWrap
(
async
(
req
,
res
)
=>
{
export
const
getAllPost
=
asyncWrap
(
async
(
req
,
res
)
=>
{
...
@@ -61,13 +80,20 @@ export const getAllPost = asyncWrap(async (req, res) => {
...
@@ -61,13 +80,20 @@ export const getAllPost = asyncWrap(async (req, res) => {
return
res
.
json
(
posts
);
return
res
.
json
(
posts
);
});
});
export
const
getFiles
=
asyncWrap
(
async
(
req
,
res
)
=>
{
const
{
postId
}
=
req
.
params
;
// console.log("나는 말하는 고구마.", postId);
const
files
=
await
postDb
.
getFilesByPostId
(
postId
);
return
res
.
json
(
files
);
});
export
const
addCounts
=
asyncWrap
(
async
(
req
,
res
)
=>
{
export
const
addCounts
=
asyncWrap
(
async
(
req
,
res
)
=>
{
// console.log(req.body);
const
{
postId
}
=
req
.
params
;
const
{
postId
}
=
req
.
params
;
const
{
counts
}
=
req
.
body
as
{
const
{
counts
}
=
req
.
body
as
{
counts
:
number
;
counts
:
number
;
};
};
console
.
log
(
postId
,
counts
);
//
console.log(postId, counts);
const
updateCounts
=
await
postDb
.
addOneCount
(
postId
,
counts
);
const
updateCounts
=
await
postDb
.
addOneCount
(
postId
,
counts
);
...
@@ -128,7 +154,7 @@ export const updatePost = asyncWrap(async (reqExp, res) => {
...
@@ -128,7 +154,7 @@ export const updatePost = asyncWrap(async (reqExp, res) => {
postId
postId
);
);
console
.
log
(
"
게시글 수정 후
"
,
updatePost
);
//
console.log("게시글 수정 후", updatePost);
return
res
.
json
(
updatePost
);
return
res
.
json
(
updatePost
);
});
});
src/db/post.db.ts
View file @
35f41d6b
import
{
Types
,
ObjectId
}
from
"
mongoose
"
;
import
{
Post
,
PostType
}
from
"
../models
"
;
import
{
Post
,
PostType
}
from
"
../models
"
;
import
{
FileInfo
,
IFileInfo
}
from
"
../models
"
;
export
const
createPost
=
async
(
post
:
PostType
)
=>
{
export
const
createPost
Row
=
async
(
post
:
PostType
)
=>
{
const
newPost
ing
=
await
Post
.
create
({
const
newPost
Row
=
await
Post
.
create
({
title
:
post
.
title
,
title
:
post
.
title
,
text
:
post
.
text
,
text
:
post
.
text
,
theme
:
post
.
theme
,
theme
:
post
.
theme
,
...
@@ -9,23 +11,41 @@ export const createPost = async (post: PostType) => {
...
@@ -9,23 +11,41 @@ export const createPost = async (post: PostType) => {
user
:
post
.
user
,
user
:
post
.
user
,
date
:
post
.
date
,
date
:
post
.
date
,
counts
:
0
,
counts
:
0
,
file
:
post
.
file
,
});
});
return
newPosting
;
return
newPostRow
;
};
export
const
createFilesRow
=
async
(
originalfilename
?:
string
|
null
,
newfilename
?:
string
,
picturepath
?:
string
)
=>
{
const
newFileRow
=
await
FileInfo
.
create
({
originalfilename
:
originalfilename
,
newfilename
:
newfilename
,
picturepath
:
picturepath
,
});
// console.log("check", newFileRow);
return
newFileRow
.
_id
;
};
};
export
const
getPosts
=
async
()
=>
{
export
const
getPosts
=
async
()
=>
{
const
posts
=
await
Post
.
find
({});
const
posts
=
await
Post
.
find
({
}).
sort
({
date
:
-
1
});
return
posts
;
return
posts
;
};
};
export
const
getFilesByPostId
=
async
(
postId
:
string
)
=>
{
const
files
=
await
Post
.
findOne
({
_id
:
postId
}).
populate
(
"
file
"
);
return
files
?.
file
;
};
export
const
addOneCount
=
async
(
_id
:
string
,
counts
:
number
)
=>
{
export
const
addOneCount
=
async
(
_id
:
string
,
counts
:
number
)
=>
{
const
newCounts
=
await
Post
.
findOneAndUpdate
(
const
newCounts
=
await
Post
.
findOneAndUpdate
(
{
_id
:
_id
},
{
_id
:
_id
},
{
counts
:
counts
},
{
counts
:
counts
},
{
new
:
true
}
{
new
:
true
}
);
);
// console.log(newCounts);
return
newCounts
;
return
newCounts
;
};
};
...
...
src/db/user.db.ts
View file @
35f41d6b
import
bcrypt
from
"
bcryptjs
"
;
import
bcrypt
from
"
bcryptjs
"
;
import
{
ObjectId
}
from
"
mongoose
"
;
import
{
ObjectId
}
from
"
mongoose
"
;
import
{
IUser
,
Role
,
Post
,
User
,
Avatar
}
from
"
../models
"
;
import
{
IUser
,
Role
,
Post
,
User
,
FileInfo
}
from
"
../models
"
;
import
fs
from
"
fs/promises
"
;
import
fs
from
"
fs/promises
"
;
export
const
createUser
=
async
(
user
:
IUser
)
=>
{
export
const
createUser
=
async
(
user
:
IUser
)
=>
{
// 비밀번호 암호화
// 비밀번호 암호화
const
hash
=
await
bcrypt
.
hash
(
user
.
password
,
10
);
const
hash
=
await
bcrypt
.
hash
(
user
.
password
,
10
);
const
newAvatar
=
await
Avatar
.
create
({});
const
newAvatar
=
await
FileInfo
.
create
({});
// 사용자 역할 추가: 기본값은 "user"
// 사용자 역할 추가: 기본값은 "user"
let
userRole
=
null
;
let
userRole
=
null
;
if
(
user
.
role
)
{
if
(
user
.
role
)
{
...
@@ -82,17 +82,17 @@ export const postPicture = async (
...
@@ -82,17 +82,17 @@ export const postPicture = async (
if
(
!
(
profile
?.
avatar
===
undefined
))
{
if
(
!
(
profile
?.
avatar
===
undefined
))
{
if
(
originalfilename
===
null
)
{
if
(
originalfilename
===
null
)
{
await
Avatar
.
findByIdAndUpdate
(
profile
.
avatar
.
_id
,
{
await
FileInfo
.
findByIdAndUpdate
(
profile
.
avatar
.
_id
,
{
nickname
:
nickname
,
nickname
:
nickname
,
});
});
}
else
if
(
nickname
===
""
)
{
}
else
if
(
nickname
===
""
)
{
await
Avatar
.
findByIdAndUpdate
(
profile
.
avatar
.
_id
,
{
await
FileInfo
.
findByIdAndUpdate
(
profile
.
avatar
.
_id
,
{
originalfilename
:
originalfilename
,
originalfilename
:
originalfilename
,
newfilename
:
newfilename
,
newfilename
:
newfilename
,
picturepath
:
picturepath
,
picturepath
:
picturepath
,
});
});
}
else
{
}
else
{
await
Avatar
.
findByIdAndUpdate
(
profile
.
avatar
.
_id
,
{
await
FileInfo
.
findByIdAndUpdate
(
profile
.
avatar
.
_id
,
{
originalfilename
:
originalfilename
,
originalfilename
:
originalfilename
,
newfilename
:
newfilename
,
newfilename
:
newfilename
,
picturepath
:
picturepath
,
picturepath
:
picturepath
,
...
@@ -104,10 +104,10 @@ export const postPicture = async (
...
@@ -104,10 +104,10 @@ export const postPicture = async (
export
const
deleteUser
=
async
(
userId
:
ObjectId
)
=>
{
export
const
deleteUser
=
async
(
userId
:
ObjectId
)
=>
{
const
user
=
await
User
.
findById
(
userId
);
const
user
=
await
User
.
findById
(
userId
);
if
(
!
(
user
?
.
avatar
===
undefined
)
)
{
if
(
user
&&
user
.
avatar
)
{
const
ref
=
await
Avatar
.
findById
(
user
.
avatar
.
_id
);
const
file
=
await
FileInfo
.
findById
(
user
.
avatar
.
_id
);
await
fs
.
unlink
(
"
../travel/uploads/
"
+
ref
?.
newfilename
);
await
fs
.
unlink
(
"
../travel/uploads/
"
+
file
?.
newfilename
);
await
Avatar
.
deleteOne
({
_id
:
user
.
avatar
.
_id
});
await
FileInfo
.
deleteOne
({
_id
:
user
.
avatar
.
_id
});
await
U
ser
.
deleteOne
(
{
_id
:
userId
}
);
return
await
u
ser
.
deleteOne
();
}
}
};
};
src/models/fileinfo.model.ts
View file @
35f41d6b
import
{
model
,
Schema
}
from
"
mongoose
"
;
import
{
model
,
ObjectId
,
Schema
}
from
"
mongoose
"
;
export
interface
I
Avatar
{
export
interface
I
FileInfo
{
originalfilename
?:
string
;
originalfilename
?:
string
;
newfilename
?:
string
;
newfilename
?:
string
;
picturepath
?:
string
;
picturepath
?:
string
;
nickname
?:
string
;
nickname
?:
string
;
}
}
const
Avatar
schema
=
new
Schema
<
I
Avatar
>
({
const
schema
=
new
Schema
<
I
FileInfo
>
({
originalfilename
:
{
type
:
String
,
unique
:
true
},
originalfilename
:
{
type
:
String
},
newfilename
:
{
type
:
String
},
newfilename
:
{
type
:
String
},
nickname
:
{
type
:
String
},
nickname
:
{
type
:
String
},
picturepath
:
{
type
:
String
},
picturepath
:
{
type
:
String
},
});
});
export
default
model
<
I
Avatar
>
(
"
Avatar
"
,
Avatar
schema
);
export
default
model
<
I
FileInfo
>
(
"
FileInfo
"
,
schema
);
src/models/index.ts
View file @
35f41d6b
export
{
default
as
User
,
IUser
}
from
"
./user.model
"
;
export
{
default
as
User
,
IUser
}
from
"
./user.model
"
;
export
{
default
as
Post
,
PostType
}
from
"
./post.model
"
;
export
{
default
as
Post
,
PostType
}
from
"
./post.model
"
;
export
{
default
as
Role
}
from
"
./role.model
"
;
export
{
default
as
Role
}
from
"
./role.model
"
;
export
{
default
as
Avatar
,
IAvatar
}
from
"
./fileinfo.model
"
;
export
{
default
as
FileInfo
,
IFileInfo
}
from
"
./fileinfo.model
"
;
export
{
default
as
Mainimg
,
MainimgType
}
from
"
./mainimg.model
"
;
export
{
default
as
Mainimg
,
MainimgType
}
from
"
./mainimg.model
"
;
src/models/post.model.ts
View file @
35f41d6b
...
@@ -5,9 +5,10 @@ export interface PostType {
...
@@ -5,9 +5,10 @@ export interface PostType {
text
:
string
;
text
:
string
;
theme
:
string
;
theme
:
string
;
city
:
string
;
city
:
string
;
user
:
Types
.
ObjectId
|
string
;
date
:
Date
|
number
;
date
:
Date
|
number
;
counts
?:
number
;
counts
?:
number
;
user
:
Types
.
ObjectId
|
string
;
file
?:
Array
<
Types
.
ObjectId
>
;
}
}
const
PostSchema
=
new
Schema
<
PostType
>
({
const
PostSchema
=
new
Schema
<
PostType
>
({
...
@@ -37,6 +38,12 @@ const PostSchema = new Schema<PostType>({
...
@@ -37,6 +38,12 @@ const PostSchema = new Schema<PostType>({
type
:
Number
,
type
:
Number
,
default
:
0
,
default
:
0
,
},
},
file
:
[
{
type
:
Schema
.
Types
.
ObjectId
,
ref
:
"
FileInfo
"
,
},
],
});
});
export
default
model
<
PostType
>
(
"
Post
"
,
PostSchema
);
export
default
model
<
PostType
>
(
"
Post
"
,
PostSchema
);
src/routes/index.ts
View file @
35f41d6b
...
@@ -12,6 +12,5 @@ router.use("/auth", authRouter);
...
@@ -12,6 +12,5 @@ router.use("/auth", authRouter);
router
.
use
(
"
/posts
"
,
postRouter
);
router
.
use
(
"
/posts
"
,
postRouter
);
router
.
use
(
"
/profile
"
,
profileRouter
);
router
.
use
(
"
/profile
"
,
profileRouter
);
router
.
use
(
"
/mainimg
"
,
mainimgRouter
);
router
.
use
(
"
/mainimg
"
,
mainimgRouter
);
//posting함수 -> mongodb에 posts json형식으로 저장
export
default
router
;
export
default
router
;
src/routes/post.route.ts
View file @
35f41d6b
import
express
from
"
express
"
;
import
express
from
"
express
"
;
import
{
postCtrl
,
authCtrl
}
from
"
../controllers
"
;
import
{
postCtrl
,
authCtrl
,
fileInfoCtrl
}
from
"
../controllers
"
;
const
router
=
express
.
Router
();
const
router
=
express
.
Router
();
router
.
route
(
"
/
"
).
post
(
authCtrl
.
requireLogin
,
postCtrl
.
postCreate
);
router
.
route
(
"
/
"
).
post
(
authCtrl
.
requireLogin
,
postCtrl
.
createFileAndPost
);
router
.
route
(
"
/
"
).
get
(
postCtrl
.
getAllPost
);
router
.
route
(
"
/
"
).
get
(
postCtrl
.
getAllPost
);
router
.
route
(
"
/files/:postId
"
).
get
(
authCtrl
.
requireLogin
,
postCtrl
.
getFiles
);
// router.param("postId", postCtrl.userByPostId);
router
router
.
route
(
"
/:postId
"
)
.
route
(
"
/:postId
"
)
.
post
(
authCtrl
.
requireLogin
,
postCtrl
.
addCounts
)
.
post
(
authCtrl
.
requireLogin
,
postCtrl
.
addCounts
)
.
get
(
authCtrl
.
requireLogin
,
postCtrl
.
getOnePost
)
.
get
(
authCtrl
.
requireLogin
,
postCtrl
.
getOnePost
)
.
delete
(
authCtrl
.
requireLogin
,
postCtrl
.
deleteOnePost
)
// authenticate
.
delete
(
authCtrl
.
requireLogin
,
postCtrl
.
deleteOnePost
)
//
+
authenticate
.
put
(
authCtrl
.
requireLogin
,
postCtrl
.
updatePost
);
.
put
(
authCtrl
.
requireLogin
,
postCtrl
.
updatePost
);
router
.
param
(
"
postId
"
,
postCtrl
.
userByPostId
);
router
.
param
(
"
postId
"
,
postCtrl
.
userByPostId
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment