1
1
import { auth } from "@/firebase/firebaseAuth" ;
2
2
import { db } from "@/firebase/firebaseDB" ;
3
- import { createUserWithEmailAndPassword , signInWithEmailAndPassword } from "firebase/auth" ;
4
- import { collection , doc , getDoc , getDocs , query , setDoc , where } from "firebase/firestore" ;
3
+ import { storage } from "@/firebase/firebaseStorage" ;
4
+ import { v4 as uuidv4 } from "uuid" ;
5
+ import {
6
+ GoogleAuthProvider ,
7
+ createUserWithEmailAndPassword ,
8
+ deleteUser ,
9
+ sendEmailVerification ,
10
+ signInWithEmailAndPassword ,
11
+ signInWithPopup ,
12
+ } from "firebase/auth" ;
13
+ import {
14
+ addDoc ,
15
+ collection ,
16
+ deleteDoc ,
17
+ doc ,
18
+ getDoc ,
19
+ getDocs ,
20
+ query ,
21
+ serverTimestamp ,
22
+ setDoc ,
23
+ where ,
24
+ } from "firebase/firestore" ;
25
+ import { getDownloadURL , ref , uploadBytes } from "firebase/storage" ;
5
26
import { rest } from "msw" ;
27
+ import axios from "axios" ;
28
+
29
+ // type postObj = {
30
+ // email: string;
31
+ // content: string;
32
+ // dynamicKeywords: string[];
33
+ // staticKeywords: string[];
34
+ // seasonKeywords: string;
35
+ // weatherKeywords: string;
36
+ // images: object[];
37
+ // createdAt: object;
38
+ // };
6
39
7
40
export const handlers = [
8
41
// 회원가입 mocking API
9
42
rest . post ( "http://localhost:3000/signup" , async ( req , res , ctx ) => {
10
43
const { email, password, keywords, gender, height, weight, nickname } = await req . json ( ) ;
11
44
try {
12
- await createUserWithEmailAndPassword ( auth , email , password ) ;
13
- await setDoc ( doc ( db , "users" , email ) , {
14
- email,
15
- gender,
16
- height,
17
- weight,
18
- keywords,
19
- nickname,
20
- } ) ;
45
+ await createUserWithEmailAndPassword ( auth , email , password )
46
+ . then ( ( userCredential ) => {
47
+ // 이메일 인증 메일 보내기.
48
+ sendEmailVerification ( userCredential . user ) ;
49
+ } )
50
+ . then ( ( ) => {
51
+ setDoc ( doc ( db , "users" , email ) , {
52
+ email,
53
+ gender,
54
+ height,
55
+ weight,
56
+ keywords,
57
+ nickname,
58
+ } ) ;
59
+ } ) ;
21
60
return res ( ctx . status ( 201 ) , ctx . json ( { success : true } ) , ctx . json ( { message : "회원 가입에 성공하였습니다." } ) ) ;
22
61
} catch ( error ) {
23
62
return res ( ctx . status ( 400 ) , ctx . json ( { success : false } ) , ctx . json ( { message : "회원 가입에 실패하였습니다." } ) ) ;
@@ -89,15 +128,15 @@ export const handlers = [
89
128
) ;
90
129
}
91
130
} ) ,
92
- //닉네임 중복검사
131
+ // 닉네임 중복검사
93
132
rest . post ( "http://localhost:3000/nicknamecheck" , async ( req , res , ctx ) => {
94
133
const { nickname } = await req . json ( ) ;
95
134
const userRef = collection ( db , "users" ) ;
96
135
const q = query ( userRef , where ( "nickname" , "==" , nickname ) ) ;
97
136
const querySnapshot = await getDocs ( q ) ;
98
- const emailExists = ! querySnapshot . empty ;
137
+ const nicknameExists = ! querySnapshot . empty ;
99
138
try {
100
- if ( emailExists === true ) {
139
+ if ( nicknameExists === true ) {
101
140
// alert("이미 존재하는 닉네임입니다.");
102
141
return res ( ctx . status ( 200 ) , ctx . json ( { success : true , message : "이미 존재하는 닉네임입니다." } ) ) ;
103
142
} else {
@@ -112,13 +151,216 @@ export const handlers = [
112
151
) ;
113
152
}
114
153
} ) ,
115
- // 로그아웃 mocking API
116
- rest . get ( "http://localhost:3000/signout" , async ( req , res , ctx ) => {
154
+ // google 로그인 mocking API
155
+ rest . get ( "http://localhost:3000/googlelogin" , async ( req , res , ctx ) => {
156
+ const provider = new GoogleAuthProvider ( ) ;
117
157
try {
158
+ const userCredential = await signInWithPopup ( auth , provider ) ;
159
+ const { email } = userCredential . user ;
160
+ await setDoc ( doc ( db , "users" , email as string ) , {
161
+ email,
162
+ } ) ;
163
+ localStorage . setItem ( "user" , JSON . stringify ( userCredential . user ) ) ;
164
+ return res ( ctx . status ( 200 ) , ctx . json ( { success : true } ) , ctx . json ( { message : "로그인에 성공하였습니다." } ) ) ;
165
+ } catch ( error ) {
166
+ return res ( ctx . status ( 400 ) , ctx . json ( { success : false } ) , ctx . json ( { message : "로그인에 실패하였습니다." } ) ) ;
167
+ }
168
+ } ) ,
169
+ // 회원탈퇴 mocking API
170
+ rest . delete ( "http://localhost:3000/deleteuser" , async ( req , res , ctx ) => {
171
+ const user = localStorage . getItem ( "user" ) ;
172
+ const { email } = JSON . parse ( user as string ) ;
173
+ const curUser = auth . currentUser ;
174
+ try {
175
+ await deleteUser ( curUser ! ) ;
176
+ await deleteDoc ( doc ( db , "users" , email ) ) ;
118
177
localStorage . removeItem ( "user" ) ;
119
- return res ( ctx . status ( 200 ) , ctx . json ( { success : true } ) , ctx . json ( { message : "로그아웃에 성공하였습니다." } ) ) ;
178
+ return res ( ctx . status ( 200 ) , ctx . json ( { success : true } ) , ctx . json ( { message : "회원탈퇴에 성공하였습니다." } ) ) ;
120
179
} catch ( error ) {
121
- return res ( ctx . status ( 400 ) , ctx . json ( { success : false } ) , ctx . json ( { message : "로그아웃에 실패하였습니다." } ) ) ;
180
+ return res ( ctx . status ( 400 ) , ctx . json ( { success : false } ) , ctx . json ( { message : "회원탈퇴에 실패하였습니다." } ) ) ;
181
+ }
182
+ } ) ,
183
+ // 게시글 작성 mocking API
184
+ rest . post ( "http://localhost:3000/postEditor" , async ( req , res , ctx ) => {
185
+ const user = localStorage . getItem ( "user" ) ;
186
+ const { email } = JSON . parse ( user as string ) ;
187
+ const { content, dynamicKeywords, staticKeywords, seasonKeywords, weatherKeywords, uploadedImageUrls } =
188
+ await req . json ( ) ;
189
+ try {
190
+ const imagesArr = [ ] ;
191
+ for ( const file of uploadedImageUrls ) {
192
+ const response = await fetch ( file ) ;
193
+ const blob = await response . blob ( ) ;
194
+ const uniqueId = uuidv4 ( ) ;
195
+ const storageRef = ref ( storage , `postImages/${ uniqueId } ` ) ;
196
+ await uploadBytes ( storageRef , blob ) ;
197
+ const url = await getDownloadURL ( storageRef ) ;
198
+ imagesArr . push ( { url } ) ;
199
+ }
200
+ // const docRef = doc(collection(db, "posts", email, "articles"));
201
+ // await setDoc(docRef, {
202
+ // email,
203
+ // content,
204
+ // dynamicKeywords,
205
+ // staticKeywords,
206
+ // seasonKeywords,
207
+ // weatherKeywords,
208
+ // images: imagesArr,
209
+ // createdAt: serverTimestamp(),
210
+ // });
211
+ await addDoc ( collection ( db , "posts" ) , {
212
+ email,
213
+ content,
214
+ dynamicKeywords,
215
+ staticKeywords,
216
+ seasonKeywords,
217
+ weatherKeywords,
218
+ images : imagesArr ,
219
+ createdAt : serverTimestamp ( ) ,
220
+ } ) ;
221
+
222
+ return res (
223
+ ctx . status ( 200 ) ,
224
+ ctx . set ( "Content-Type" , "multipart/form-data" ) ,
225
+ ctx . json ( { success : true } ) ,
226
+ ctx . json ( { message : "게시글 작성에 성공하였습니다." } ) ,
227
+ ) ;
228
+ } catch ( error ) {
229
+ return res ( ctx . status ( 400 ) , ctx . json ( { success : false } ) , ctx . json ( { message : "게시글 작성에 실패하였습니다." } ) ) ;
230
+ }
231
+ } ) ,
232
+ //날씨 정보 불러오기 mocking API
233
+ // rest.post("http://localhost:3000/weather", async (req, res, ctx) => {
234
+ // const { lat, lon } = await req.json();
235
+ // try {
236
+ // // 현재 위치의 날씨 정보를 불러옵니다.
237
+ // // 날씨 정보가 있을 때,
238
+ // const response = await axios.get(
239
+ // `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}¤t_weather=true&timezone=auto`,
240
+ // );
241
+ // const curTemperature = response.data.current_weather.temperature;
242
+ // const weatherCode = response.data.current_weather.weathercode;
243
+ // return res(
244
+ // ctx.status(200),
245
+ // ctx.json({ success: true, message: "날씨 정보를 불러오는데 성공하였습니다.", curTemperature, weatherCode }),
246
+ // );
247
+ // } catch (error) {
248
+ // // 현재 위치의 날씨 정보를 불러오지 못했을 경우, 서울의 날씨 정보를 불러옵니다.
249
+ // const response = await axios.get(
250
+ // `https://api.open-meteo.com/v1/forecast?latitude=37.566&longitude=126.9784¤t_weather=true&timezone=auto`,
251
+ // );
252
+ // const curTemperature = response.data.current_weather.temperature;
253
+ // const weatherCode = response.data.current_weather.weathercode;
254
+ // return res(
255
+ // ctx.status(400),
256
+ // ctx.json({ success: false, message: "날씨 정보를 불러오는데 실패하였습니다.", curTemperature, weatherCode }),
257
+ // );
258
+ // }
259
+ // }),
260
+ rest . post ( "http://localhost:3000/weather" , async ( req , res , ctx ) => {
261
+ const { lat, lon } = await req . json ( ) ;
262
+ try {
263
+ // 현재 위치의 날씨 정보를 불러옵니다.
264
+ const response = await axios . get (
265
+ `https://api.open-meteo.com/v1/forecast?latitude=${ lat } &longitude=${ lon } ¤t_weather=true&timezone=auto` ,
266
+ ) ;
267
+ const curTemperature = response . data . current_weather . temperature ;
268
+ const weatherCode = response . data . current_weather . weathercode ;
269
+ return res (
270
+ ctx . status ( 200 ) ,
271
+ ctx . json ( { success : true , message : "날씨 정보를 불러오는데 성공하였습니다." , curTemperature, weatherCode } ) ,
272
+ ) ;
273
+ } catch ( error ) {
274
+ return res ( ctx . status ( 400 ) , ctx . json ( { success : false , message : "날씨 정보를 불러오는데 실패하였습니다." } ) ) ;
122
275
}
123
276
} ) ,
277
+ // 게시글 불러오기 mocking API (한명의 유저가 작성한 게시글)
278
+ // axios.get(`/posts?email=${email}`)
279
+ rest . get ( `http://localhost:3000/userPosts` , async ( req , res , ctx ) => {
280
+ const email = req . params . email ;
281
+ try {
282
+ const userPosts : object [ ] = [ ] ;
283
+
284
+ const q = query ( collection ( db , "posts" ) , where ( "email" , "==" , email ) ) ;
285
+ const querySnapshot = await getDocs ( q ) ;
286
+ querySnapshot . forEach ( ( doc ) => {
287
+ userPosts . push ( doc . data ( ) ) ;
288
+ } ) ;
289
+ return res (
290
+ ctx . status ( 200 ) ,
291
+ ctx . json ( { success : true , message : "게시글을 불러오는데 성공하였습니다." , userPosts } ) ,
292
+ ) ;
293
+ } catch ( error ) {
294
+ return res ( ctx . status ( 400 ) , ctx . json ( { success : false , message : "게시글을 불러오는데 실패하였습니다." } ) ) ;
295
+ }
296
+ } ) ,
297
+ // 날씨 추천 게시글 불러오기 mocking API (모든 유저가 작성한 게시글 중에서 seasonKeywords와 weatherKeywords 일치하는 게시글)
298
+ // axios.get(`/posts?seasonKeywords=${seasonKeywords}&weatherKeywords=${weatherKeywords}`)
299
+ // Url은 위와 같이 작성해주시면 될 것 같습니다.
300
+ // ft/main 브랜치를 기준으로 설명드리면,
301
+ // 현재 날씨 정보는 Weather.tsx에서 불러오고 있기 떄문에,
302
+ // ${}안에 들어가는 날씨 키워드와 계절 키워드는 Weather.tsx에서 불러온 날씨 정보를 기준으로 작성하시면 될 것 같습니다.
303
+ // 다만, 날씨 추천 게시글 불러오기 api가 main 페이지에서 호출되어야 하므로,
304
+ // 1. Weather.tsx에서 날씨 정보 호출
305
+ // 2. 날씨 정보를 토대로, 날씨 키워드와 계절 키워드를 설정하고,
306
+ // 3. 이때, 날씨 키워드와 계절 키워드는 main페이지에서 사용되어야 하므로, Recoil을 사용하여 전역 상태로 만들어 주시고,
307
+ // 4. Main 페이지에서 위의 axios 요청에 필요한 키워드를 전역 상태로 만들어둔 키워드를 사용하여 요청하시면 될 것 같습니다.
308
+ rest . get ( `http://localhost:3000/posts` , async ( req , res , ctx ) => {
309
+ const seasonKeywords = req . url . searchParams . get ( "seasonKeywords" ) ;
310
+ const weatherKeywords = req . url . searchParams . get ( "weatherKeywords" ) ;
311
+
312
+ try {
313
+ const posts : object [ ] = [ ] ;
314
+ const seasonq = query (
315
+ collection ( db , "posts" ) ,
316
+ where ( "seasonKeywords" , "==" , [ seasonKeywords ] ) ,
317
+ where ( "weatherKeywords" , "==" , [ weatherKeywords ] ) ,
318
+ ) ;
319
+ const seasonQuerySnapshot = await getDocs ( seasonq ) ;
320
+ seasonQuerySnapshot . forEach ( ( doc ) => {
321
+ const data = doc . data ( ) ;
322
+ posts . push ( data ) ;
323
+ } ) ;
324
+ // const weatherq = query(collection(db, "posts"), where("weatherKeywords", "==", weatherKeywords));
325
+ // const weatherQuerySnapshot = await getDocs(weatherq);
326
+ // weatherQuerySnapshot.forEach((doc) => {
327
+ // posts.push(doc.data());
328
+ // });
329
+ return res ( ctx . status ( 200 ) , ctx . json ( { success : true , message : "게시글을 불러오는데 성공하였습니다." , posts } ) ) ;
330
+ } catch ( error ) {
331
+ return res ( ctx . status ( 400 ) , ctx . json ( { success : false , message : "게시글을 불러오는데 실패하였습니다람쥐." } ) ) ;
332
+ }
333
+ } ) ,
334
+ // 게시글 불러오기 mocking API (키워드에 따른 게시글 조회)
335
+ // rest.get(`http://localhost:3000/posts/`, async (req, res, ctx) => {
336
+ // // url에 담긴 키워드를 가져옵니다.
337
+ // const { staticKeywords, seasonKeywords, weatherKeywords } = req.params;
338
+ // try {
339
+ // // dynamicKeywords, staticKeywords, seasonKeywords, weatherKeywords가 여러개일 경우, 각각의 키워드를 배열로 만듭니다.
340
+ // // dynamicKeywords가 존재할 경우, dynamicKeywords에 해당하는 게시글을 불러옵니다.
341
+ // if (staticKeywords) {
342
+ // // staticKeywords가 존재할 경우, staticKeywords에 해당하는 게시글을 불러옵니다.
343
+ // const posts: object[] = [];
344
+
345
+ // for (const keywords of staticKeywords) {
346
+ // const q = query(collection(db, "posts"), where("staticKeywords", "==", keywords));
347
+ // const querySnapshot = await getDocs(q);
348
+ // querySnapshot.forEach((doc) => {
349
+ // posts.push(doc.data());
350
+ // });
351
+ // }
352
+ // // posts에 담긴 게시글 중에서 seasonKeywords와 weatherKeywords가 일치하는 게시글만 불러옵니다.
353
+ // const filteredPosts = posts.filter((post) => {
354
+ // return post.seasonKeywords === seasonKeywords && post.weatherKeywords === weatherKeywords;
355
+ // });
356
+ // return res(
357
+ // ctx.status(200),
358
+ // ctx.json({ success: true, message: "게시글을 불러오는데 성공하였습니다.", filteredPosts }),
359
+ // );
360
+ // }
361
+ // } catch (error) {
362
+ // return res(ctx.status(400), ctx.json({ success: false, message: "게시글을 불러오는데 실패하였습니다." }));
363
+ // }
364
+ // }),
365
+ // 게시글 불러오기 mocking API (팔로잉한 유저가 작성한 게시글)
124
366
] ;
0 commit comments