오늘은 어제 만든 로그인을 테스트해보겠습니다.
axios로 로컬로 백엔드랑 연결해볼게요
우선 axios를 설치합니다.
npm i axios
일단 지금은 간단하게만 axiosInstance를 만들어볼게요
import axios from "axios";
export const axiosInstance = axios.create({
baseURL: "http://localhost:3000",
headers: {
"Content-Type": "application/json",
},
});
axiosInstance.interceptors.request.use(async (config) => {
return config;
});
axiosInstance.interceptors.response.use(async (response) => {
return response;
});
그런 담에 로컬 백엔드를 킵니다.
그리고 로그인 로직에 axiosInstance를 넣어봅시다.
try {
const res = await axiosInstance.post("auth/login", {
email,
password,
});
} catch (e) {
console.log("error:", e);
alert(e);
}
자 문제가 생겼습니다.
저는 노트북 와이파이로 개발중인데요. 그리고 expo go 어플로 폰으로 테스트하고 있습니다.
여기서 네트워크 오류가 계속 나네요.
이제 환경에 맞게 url을 수정해봅시다.
우선 시작 검색창에 cmd(명령 프롬프트)를 실행시킵니다
여기서 ipconfig 명령어를 실행시키면 ip주소관련해서 나올건데요
ipv4 주소를 복붙해줍니다.
이제 보니 expo 실행시켜도 ip주소가 나오네요.
Metro waiting on exp://[ip]:8081
다시 baseUrl을 수정합시다.
baseURL: "http://아이피주소 : 포트번호(3000)",
이렇게 수정하니 기존 가입했던 아이디로 로그인이 성공되었네요!
저처럼 카페나, 청년공간 등 옮겨다니며 하시는 분들은 아이피 주소를 수정해주세요 ㅎㅎ
env사용하실 분들은 아래처럼 사용하시면 됩니다!
baseURL: process.env.EXPO_PUBLIC_API_URL,
하지만 저 코드가지고는 성공했는지, 알 수 없겠죠? 우선 zustand를 사용해서 lobby로 넘어갈 때 닉네임만 상단에 추출해봅시다.
Zustand를 선택한 이유는 간편하고 provider설정을 안해줘도 되니 이슈가 적을 것 같아서 입니다.
우선 설치해봅시다.
npm i zustand
다음으로 store를 만들어봅시다.
import { create } from "zustand";
import { UserType } from "../types/userType";
export interface UserStoreType {
user: UserType;
accessToken: string;
setUser: (user: UserType) => void;
setAccessToken: (accessToken: string) => void;
resetUser: () => void;
}
const userStore = create<UserStoreType>((set) => ({
user: {
id: 0,
email: "",
nickname: "",
profile_image: "",
introduction: "",
},
accessToken: "",
setUser: (user: UserType) => set({ user }),
setAccessToken: (accessToken: string) => set({ accessToken }),
resetUser: () =>
set({
user: {
id: 0,
email: "",
nickname: "",
profile_image: "",
introduction: "",
},
}),
}));
export default userStore;
백엔드에서 어떤 타입이 넘어올지 아니 UserType도 지정해봅시다.
export interface UserType {
id: number;
email: string;
nickname: string;
profile_image?: string;
introduction?: string;
}
export interface ResponseUserType extends UserType {
accessToken: string;
refreshToken?: string;
}
export interface LoginType {
email: string;
password: string;
}
export interface SignUpType extends LoginType {
nickname: string;
}
혹시 몰라 여러 타입을 미리 만들어 봤습니다.
자 이제 성공했을 때 저장하고 로비로 이동하는 로직을 추가해봅시다.
const data: ResponseUserType = await res.data;
setUser({
id: data.id,
email: data.email,
nickname: data.nickname,
profile_image: data.profile_image,
introduction: data.introduction,
});
setAccessToken(data.accessToken);
router.push("/lobby");
로비는 간단하게 닉네임이 보이게 작성해봅시다.
import { View, Text } from "react-native";
import React from "react";
import userStore from "../../assets/stores/userStore";
export default function Lobby() {
const { user } = userStore();
return (
<View>
<Text>{user.nickname}님 환영합니다.</Text>
</View>
);
}
이렇게하면?
이렇게 나옵니다. 좋아요 로그인이 성공한거예요! ㅎㅎ
자 이제 회원가입 먼저 해볼까요??
회원가입도 로그인이랑 똑같아요 디자인은 거의 똑같고 로직만 살짝 다르니까요 ㅎㅎ
우선 회원가입 폼을 만들어봅시다.
전체코드입니다.
import { View, Text, Pressable, StyleSheet } from "react-native";
import React, { useState } from "react";
import InputBox from "../../../assets/components/Input/InputBox";
import { Link, router } from "expo-router";
import { BASE_COLOR } from "../../../assets/constants/color";
import { checkSignUpMessage } from "../../../assets/utils/checkSignMessage";
import { axiosInstance } from "../../../assets/api/axiosInstance";
import { changeAxiosErrorMessage } from "../../../assets/utils/changeAxiosErrorMessage";
export default function SignupForm() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [passwordCheck, setPasswordCheck] = useState("");
const [nickname, setNickname] = useState("");
const submitSignup = async () => {
const checkMessage = checkSignUpMessage(
email,
nickname,
password,
passwordCheck
);
if (checkMessage !== "") {
alert(checkMessage);
return;
}
// 회원가입 API 호출
try {
const res = await axiosInstance.post("auth/signup", {
email,
nickname,
password,
});
if (res.status === 201) {
alert("회원가입이 완료되었습니다.");
router.replace("/login");
}
} catch (e: unknown) {
const message = changeAxiosErrorMessage(e);
alert(message);
}
};
return (
<View style={styles.form}>
<InputBox
labelName="이메일"
placeholder="이메일을 입력해주세요"
value={email}
onChangeText={(value: string) => setEmail(value)}
/>
<InputBox
labelName="닉네임"
placeholder="닉네임을 입력해주세요"
value={nickname}
onChangeText={(value: string) => setNickname(value)}
/>
<InputBox
labelName="비밀번호"
placeholder="비밀번호를 입력해주세요"
value={password}
onChangeText={(value: string) => setPassword(value)}
secureTextEntry
/>
<InputBox
labelName="비밀번호 확인"
placeholder="한번 더 비밀번호를 입력해주세요"
value={passwordCheck}
onChangeText={(value: string) => setPasswordCheck(value)}
secureTextEntry
/>
<Pressable style={styles.submitButton} onPress={submitSignup}>
<Text style={styles.submitButtonText}>회원가입</Text>
</Pressable>
<Link href={{ pathname: "/login" }} style={styles.signLinkText}>
<Text style={styles.signLinkText}>로그인하러 가기</Text>
</Link>
</View>
);
}
const styles = StyleSheet.create({
form: {
flex: 1,
justifyContent: "flex-start",
},
submitButton: {
backgroundColor: BASE_COLOR.LIGHT,
padding: 20,
borderRadius: 15,
alignItems: "center",
marginHorizontal: 20,
marginTop: 30,
},
submitButtonText: {
fontSize: 20,
color: BASE_COLOR.LIGHT_TEXT,
},
signLinkText: {
color: BASE_COLOR.LIGHT,
fontSize: 20,
textAlign: "center",
padding: 20,
},
});
여기서 checkSignUpMessage는 아래와 같이 짰습니다.
import { AUTH_ERROR_MESSAGE } from "../constants/message";
const EMAIL_REGEX = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/i;
const PASSWORD_REGEX = /^(?=.*[a-zA-Z])(?=.*[0-9]).{8,}$/;
const checkEmail = (email: string) => {
if (!email) {
return AUTH_ERROR_MESSAGE.NOT_EMAIL;
}
if (!EMAIL_REGEX.test(email)) {
return AUTH_ERROR_MESSAGE.INVALID_EMAIL;
}
return "";
};
const checkPassword = (password: string) => {
if (!password) {
return AUTH_ERROR_MESSAGE.NOT_PASSWORD;
}
if (!PASSWORD_REGEX.test(password)) {
return AUTH_ERROR_MESSAGE.INVALID_PASSWORD;
}
return "";
};
export const checkLoginMessage = (email: string, password: string) => {
const checkedEmail = checkEmail(email);
const checkedPassword = checkPassword(password);
if (checkedEmail !== "") {
return checkedEmail;
}
if (checkedPassword !== "") {
return checkedPassword;
}
return "";
};
export const checkSignUpMessage = (
email: string,
nickname: string,
password: string,
passwordCheck: string
) => {
const checkedEmail = checkEmail(email);
const checkedPassword = checkPassword(password);
console.log(email, nickname, password, passwordCheck);
if (checkedEmail !== "") {
return checkedEmail;
}
if (checkedPassword !== "") {
return checkedPassword;
}
if (nickname === "") {
return AUTH_ERROR_MESSAGE.NOT_NICKNAME;
}
if (password !== passwordCheck) {
return AUTH_ERROR_MESSAGE.NOT_PASSWORD_MATCH;
}
return "";
};
그리고 changeAxiosErrorMessage는 아래처럼 간단하게 했습니다.
import { AxiosError } from "axios";
interface AxiosErrorResponseType {
data: {
message: string;
};
}
export const changeAxiosErrorMessage = (error: unknown) => {
const e = error as AxiosError;
const response = e.response as AxiosErrorResponseType;
if (response !== undefined) {
return response.data["message"] as string;
}
return "알 수 없는 오류가 발생했습니다.";
};
자 이제 디자인을 볼까요?
좋아요 동작도 나름하고 나중에 더 추가될 수 있기 때문에 index(회원가입)은 아래처럼 scrollview를 넣었습니다.
import React from "react";
import { ScrollView, StyleSheet, View } from "react-native";
import SignHeader from "../../assets/components/Sign/SignHeader";
import { BASE_COLOR } from "../../assets/constants/color";
import SignupForm from "./_components/SignupForm";
export default function LoginPage() {
return (
<View style={styles.container}>
<SignHeader />
<View style={styles.scrollView}>
<ScrollView>
<SignupForm />
</ScrollView>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: BASE_COLOR.DEEP,
},
scrollView: {
flex: 3,
},
});
오늘은 여기까지 해야겠어요.
다들 고생많으셨습니다! ㅎㅎ
백엔드 전체코드 보러가기
SiWooJinSeok/Quiz-Galaxy-Planet-BackEnd (github.com)
GitHub - SiWooJinSeok/Quiz-Galaxy-Planet-BackEnd
Contribute to SiWooJinSeok/Quiz-Galaxy-Planet-BackEnd development by creating an account on GitHub.
github.com
expo 전체코드 보러가기
SiWooJinSeok/QuizGalaxyPlanet: QuizGalaxyPlanet App (github.com)
GitHub - SiWooJinSeok/QuizGalaxyPlanet: QuizGalaxyPlanet App
QuizGalaxyPlanet App. Contribute to SiWooJinSeok/QuizGalaxyPlanet development by creating an account on GitHub.
github.com
'퀴즈앱 개발기 > RN' 카테고리의 다른 글
퀴즈앱 9일차 이메일 인증( + OTP식으로 구현) (0) | 2024.07.03 |
---|---|
퀴즈앱 8일차 (+ 머리가 안돌아가는 날..) (0) | 2024.06.28 |
퀴즈앱 6일차 (0) | 2024.06.26 |
퀴즈앱 5일차(Expo router 사용법) (0) | 2024.06.25 |