📝 오늘 한 일
✔️ about페이지 디자인 이슈 해결 (isDesktop, isLaptop 선언 오류 해결)
✔️ 카드 4장 isDesktop일땐 Justify-end 안하고싶은데 ㅠㅠ -> 해결
✔️ about페이지 laptop 디자인 마무리
(custom hook, zustand 생성)
✔️ 포인트 획득 로직 추가 (개인액션, 게시글 등록, 댓글 등록)
✔️ 빌드 (yarn build
,yarn start
)
✔️ 배포 (주소 나만 볼수있는거 <-> 공용 주소 따로 존재하는걸 알게됨)
✔️ 포인트 획득 로직추가 (개인액션 등록, 게시글 등록, 댓글 등록)
✔️ alert 전부다 모달창으로 리팩토링 ㄷㄷ 엄청 많았다 (아래의 x2배)
문제상황 : about페이지에서 화면이 줄어도 늘어도, 변해야 할 내용들이 그냥 1920 기준 픽셀 혹은 1020 기준 픽셀로 고정되어 있는 상황 발생
원인 :
const isDesktop = window.innerWidth >= 1920;
이런식으로 선언해서는 화면 사이즈가 변했다는 사실을 바로바로 반영하지 못하기 때문
해결 : useEffect로 화면크기 변경 있을 때마다 바로바로 캐치할 수 있도록 만들기
window.addEventListener("resize", handleResize);
window.innerWidth
를 set해주기clean up 함수
생성추가 내용
// page.tsx - about페이지
const AboutPage = () => {
// custom hook - 현재 브라우저 화면의 사이즈 상태 가져오기
const { isDesktop, isLaptop, isMobile } = useResponsive();
//...생략
// responsiveStore.ts - zustand로 브라우저 화면사이즈, set함수 관리
import { create } from "zustand";
interface Responsive {
isDesktop: boolean;
isLaptop: boolean;
isMobile: boolean;
setWindowSize: (width: number) => void;
}
export const useResponsiveStore = create<Responsive>((set) => ({
isDesktop: false,
isLaptop: false,
isMobile: false,
setWindowSize: (width: number) => {
set({
isDesktop: width >= 1920,
isLaptop: width >= 1020 && width < 1920,
isMobile: width < 1020 && width >= 360,
});
},
}));
// index.ts - custom hook (useEffect로 화면사이즈 변동 파악, 화면별 boolean return)
import { useEffect } from "react";
import { useResponsiveStore } from "@/app/_store/responsiveStore";
export const useResponsive = () => {
const setWindowSize = useResponsiveStore((state) => state.setWindowSize);
useEffect(() => {
// ⭐️ 화살표함수는 this 바인딩을 생략하기 때문에 function기본형 함수 써야함
function handleResize() {
console.log("리사이즈가 동작하고 있습니다.");
// 여기에 set~~ 코드 삽입
setWindowSize(window.innerWidth);
}
// window에 이벤트 등록 (browser가 기본적으로 갖고있는 이벤트 중 하나 "resize")
// resize가 일어날때 handleResize 함수가 동작함
// (리액트에서 윈도우 사이즈 실시간으로 재는 기능 제공하는지 몰라 js문법 사용)
window.addEventListener("resize", handleResize);
handleResize();
// cleanup 함수
return () => {
window.removeEventListener("resize", handleResize);
};
}, [setWindowSize]);
// (기존에 zustand에서 가져와 쓰는 방식 예시)
// const isDesktop = useResponsiveStore((state)=>state.isDesktop);
// (custom hook에서 return하는게 헷갈린다면 참고하도록 풀어써본 코드)
// const resultObj = useResponsiveStore(function(state) {
// let isDesktop = state.isDesktop;
// let isLaptop = state.isLaptop;
// let isMobile = state.isMobile;
// return {
// isDesktop: isDesktop,
// isLaptop: isLaptop,
// isMobile: isMobile
// }
// });
const resultObj = useResponsiveStore((state) => {
return {
isDesktop: state.isDesktop,
isLaptop: state.isLaptop,
isMobile: state.isMobile,
};
});
return resultObj;
};
// AddPostModal.tsx : _components/ community
// 컴포넌트에서 포인트 업데이트시 - mode도 함께 인자로 전달
// 🧡 { mode: "" } 이렇게 보내주지만, 인자로 받을때는 option:{ mode : "" }로 받는다.
await updateUserPoint(loggedInUserUid, { mode: "addPost" });
// add-api.ts
// 2. point 업데이트 - mode에 따라 업데이트되는 포인트가 달라짐
// (다른 컴포넌트에서도 사용하기 때문에 로직 분리)
export const updateUserPoint = async (
loggedInUserUid: string,
option: { mode: string },
) => {
try {
// 🧡 인자로 받아온 mode 꺼내기
const { mode } = option;
// 포인트 가져오기
const { data, error } = await supabase
.from("users")
.select("point")
.eq("id", loggedInUserUid);
if (error) {
throw error;
}
const point = data[0].point;
let updatedPoint;
// 🧡 mode에 따라 달라지는 포인트 업데이트
// (댓글은 200포인트, 개인액션 및 게시글 등록은 500포인트 업데이트)
if (point && mode === "comment") {
updatedPoint = point + 200;
} else if (point && (mode === "addAction" || mode === "addPost")) {
updatedPoint = point + 500;
}
const { error: updatePointError } = await supabase
.from("users")
.update({ point: updatedPoint })
.eq("id", loggedInUserUid);
if (updatePointError) {
throw error;
}
} catch (error) {
console.error("Error inserting data:", error);
throw error;
}
};
📝 프로젝트 전반에서 사용하고 있는
기본 alert창
을 디자인적으로 더 예쁜모달창
으로 리팩토링하기 위해 공용 모달창 생성
-> alert을 사용중인 모든 컴포넌트에서 사용할 수 있게 공용 모달창으로 만듦
// AddPostModal.tsx : _components/community
// (각 컴포넌트에서 사용하는 예시)
import React, { useState } from "react";
import AlertModal from "./AlertModal";
// 🧡 alert 대체 모달창을 위한 상태관리
const [isOpenAlertModal, setIsOpenAlertModal] = useState(false);
const [message, setMessage] = useState("");
// 🧡 문구 설정, 모달창 열기
// alert("로그인이 필요합니다.");
setMessage("로그인이 필요한 서비스입니다.");
setIsOpenAlertModal(true);
// 🧡 모달창 컴포넌트는 jsx 최 하단에 위치
{isOpenAlertModal && (
<AlertModal
isOpen={isOpenAlertModal}
onClose={() => setIsOpenAlertModal(false)}
message={message}
/>
)}
// AlertModal.tsx : _components/community
// (모달창 컴포넌트)
import React from "react";
import Image from "next/image";
import { Button, Modal, ModalBody, ModalContent } from "@nextui-org/react";
import logoImg from "../../_assets/image/logo_icon/logo/gray.png";
interface PointModalProps {
isOpen: boolean;
onClose: () => void;
message: string;
}
const AlertModal: React.FC<PointModalProps> = ({
isOpen,
onClose,
message,
}) => {
return (
<>
<Modal placement="center" isOpen={isOpen} onOpenChange={onClose}>
<ModalContent>
{(onClose) => (
<>
<ModalBody className="flex flex-col gap-14 justify-between items-center py-[40px]">
<Image src={logoImg} alt="alert" className="w-[15%] h-[7%]" />
<p className="font-bold text-center text-[16px]">{message}</p>
<Button
type="submit"
className="text-gray-500 rounded-full !w-[140px] h-[33px] border border-gray-400 bg-[#EFEFEF]"
onClick={() => {
onClose();
}}
>
확인
</Button>
</ModalBody>
</>
)}
</ModalContent>
</Modal>
</>
);
};
export default AlertModal;
기존 : 기본 alert 창
변경 후 : 공통 modal창