[감정일기장/ React] 웹 스토리지 이용과 배포 방법

diaryPreview.gif
이번에 만들었던 감정 일기장.

최근에 만든 React 기반 일기장을 웹 스토리지를 이용해 데이터에 브라우저에 저장하고,  Vercel에 배포하기까지의 과정을 공유하려고 해요. (웹 스토리지를 이용하면 서버 없이도 사용자가 작성한 일기를 브라우저에 안전하게 저장하고 불러올 수 있음!) 

1. localStorage로 일기 저장하기

서버를 따로 두지 않고 데이터를 저장하려면 웹 스토리지(localStorage) 를 사용하는 게 간단하고 편리해요.
localStorage는 브라우저에 key-value 형식으로 데이터를 저장할 수 있는 저장소입니다.

localStorage 기본 사용법

// 저장하기
localStorage.setItem("key", "value");

// 불러오기
const data = localStorage.getItem("key");

// 삭제하기
localStorage.removeItem("key");

// 전부 삭제
localStorage.clear();

하지만 일기처럼 객체/배열 형태의 데이터를 저장할 땐, 문자열로 변환해주는 과정이 필요해요.

객체 저장 & 불러오기 (with JSON) - 실제 적용 코드

 useReducer를 사용해 상태를 관리하면서 localStorage에 데이터를 저장하고 불러오는 흐름!

// reducer 함수: 일기 데이터를 관리하는 핵심 로직
function reducer(state, action) {
  let nextState;
  switch (action.type) {
    case "INIT": {
      return action.data; // 초기화 시 저장된 데이터로 상태를 설정
    }
    case "Create": {
      nextState = [action.data, ...state];
      break;
    }
    case "UPDATE": {
      nextState = state.map((item) =>
        String(item.id) === String(action.data.id) ? action.data : item
      );
      break;
    }
    case "DELETE": {
      nextState = state.filter((item) => String(item.id) !== String(action.id));
      break;
    }
    default:
      return state;
  }

  // 상태가 변경될 때마다 localStorage에 저장 (JSON 문자열로 변환해서 저장)
  localStorage.setItem("diary", JSON.stringify(nextState));
  return nextState;
}

function App() {
  const [isLoading, setIsLoading] = useState(true);
  const [data, dispatch] = useReducer(reducer, []);
  const idRef = useRef(0);

  useEffect(() => {
    const storedData = localStorage.getItem("diary"); // 저장된 문자열 가져오기
    if (!storedData) {
      setIsLoading(false); // 데이터가 없으면 로딩 해제하고 종료
      return;
    }
    const parsedData = JSON.parse(storedData); // 문자열 →  객체로 변환
    if (!Array.isArray(parsedData)) {
      setIsLoading(false); //배열이 아니면 중단
      return;
    }
    // 최대 ID값 계산 → 새 항목 생성 시 ID 겹치지 않도록
    let maxId = 0;
    parsedData.forEach((item) => {
      if (Number(item.id) > maxId) {
        maxId = Number(item.id);
      }
    });
    idRef.current = maxId + 1;
	
    // 초기 데이터로 상태 설정
    dispatch({
      type: "INIT",
      data: parsedData, // 불러온 데이터를 반영
    });
    setIsLoading(false);
  }, []);

// ...onCreate, onUpdate, onDelete 등 dispatch 처리

 

포인트 정리

작업  코드에서 하는 일 
localStorage.setItem() 객체를 JSON 문자열로 변환해 저장
localStorage.getItem() 저장된 JSON 문자열 불러오기
JSON.parse() 문자열을 JavaScript 객체로 변환
useEffect() 앱이 처음 실행될 때 데이터 불러오기

2. 배포 전 프로젝트 빌드

npm run build

etc-image-1

터미널에 작성하고 나면 해당과 같은 배포 가능한 정적 파일이 준비가 된다. 이후 Vercel에 연결해서 배포만 하면 된다!


3. Vercel CLI 설치하기

npm i -g vercel
  • 맥을 사용할 경우 앞에 sudo를 붙여줍니다.
  • -g를 붙여서 전역 설치!

4. Vercel 로그인하기

vercel login
  • vercel에 가입된 본인 계정으로 로그인 해주면 됩니다. 저는 github으로 로그인 했어요!
  • 이메일의 경우 이메일 주소를 입력하면 인증 메일이 오니 확인해주세요~!

5. 프로젝트 폴더에서 vercel 실행하기

vercel

처음 실행하면 몇 가지 질문을 받게 됩니다. 

질문 답변
"Set up and deploy?"
(이 위치에 있는 파일들을 배포하시겠습니까?)
Y 또는 Enter
"Which scope should it be deployed to?"
(어떤 계정에 배포 하시겠습니까?) 
로그인 한 계정 선택
"Link to existing project?"
(이미 존재하는 프로젝트에 연결하시겠습니까?)
N
"What’s your project’s name?"
(프로젝트의 네임은 무엇입니까?)
원하면 이름 변경 (아니면 기본값)
"In which directory is your code located?"
(어느 위치에 코드가 위치해있습니까?)
대부분 . 입력 (현재 폴더)
"Want to modify these settings?"
(설정을 변경하시겠습니까?)
n (저는 바이트를 사용해서 리액트 프로젝트를 만들었기 때문에, vercel에서 빌드 설정을 자동으로 해주었습니다.)

6. 배포 완료!

질문에 모두 답하고 나면 배포가 완료됩니다! 배포를 모두 마치면, production: 주소가 하나 뜹니다.

근데 저는 여기서 이 주소로 접속하면 정상적으로 앱이 열리긴 하지만, 문제가 있었어요. 카카오톡이나 SNS에 링크를 보낼 때 썸네일 미리보기 이미지가 뜨지 않고, 다른 사람이 접속할 때 access 요청을 하라고 뜨더라구요...?

그래서 더 찾아보니 Vercel의 기본 설정 중 ‘배포 보호(Deployment Protection)’ 기능이 그 원인이었습니다.

해결 방법

etc-image-2

[프로젝트] → Settings → Deployment Protection

Vercel 프로젝트 페이지에서 해당 이미지와 같이 다음 경로로 이동해서 Vercel authentication이라는 항목을 보면,
production 배포 주소 외에 다음과 같은 형식의 공개 주소들이 함께 제공됩니다. 이 주소를 사용하면 문제가 해결됩니다!


이번 프로젝트를 하면서도 새로운 걸 많이 배운 것 같아요. 투두리스트에서 쪼끔 뭐가 더 많아졌다고 상태관리가 더 다양해지는 점도 그렇고... 그래도 프로젝트를 하나 만들고, 배포까지 하고나니 정말 뿌듯했습니다! 끝까지 하는건 늘 중요한 것 같아요. 이번에 제가 만든 감정 일기장은 여기서 확인 가능합니다~!