티스토리 뷰

회고

[팀프로젝트] 올해까치 회고

2021bong 2022. 12. 30. 12:02

올해까치 인트로 이미지

올해까치

 

올해까지?"올해까치"

까치와 함께 올해 목표를 세워보고 알찬 한해를 보내보세요.

thisyearkkachi.netlify.app

 

 

프로젝트 소개

신년 목표를 세우고 6개월 뒤 리마인드 하고 싶은 사람을 위한 신년 목표 1개월 시즌 메일링 서비스

 

  • 제작 기간 : 22.12.09 - 12.28 (약 3주)
  • 서비스 기간 : 22.12.28 - 23.1.31 (약 1달)
  • 팀원 : Design 1명, FE 2명, BE 1명
  • 협업방식
    • 툴 : 피그마, 노션
    • 회의 : 첫 주는 기획 회의를 위해 2-3일에 한번 다 같이 회의 진행, 그 후 단체 회의는 1주에 한 번 진행. 포지션 별로 회의가 필요할 때 개별적으로 약속을 잡아 회의 진행
  • 사용언어 및 개발환경
    • FE : Vite, React.js, Typescript, Recoil
    • BE : Node.js, Typescript, TypeORM, Express, CryptoJS
  • 프로젝트 목적
    • 새해에 신년 목표를 세워도 시간이 지나면 잊게 되는데 6개월 뒤 메일을 보내 목표를 다시 되새기도록 하여 새해 목표를 지킬 수 있도록 도와주는 서비스
  • GitHub : FE (private), BE (private)

 

 

맡은 역할과 기능

1. 기획

팀원 모두가 기획에 참여했고, 첫 주는 기획 회의를 진행했다.

 

  • 프로젝트 이름
    • 올해까치 : 올해(이번 해) + (~)까지, 까치의 합성어. 여러 가지 의미로 해석할 수 있음.
  • 타겟
    • 올해 목표를 정하고 반년 뒤 리마인드를 받고 싶은 사람
    • 퍼블릭하게 자신의 목표를 보여주고 싶은 심리로 SNS 공유를 유도
  • 진행 순서
    • 인트로 화면 + 서비스 설명
    • 유저 정보 입력 및 이메일 수신 동의
    • 새해에 받고 싶은 복 목록
    • 선택한 복 모달
    • 목표 적기 (최대 5개)
    • 결과 페이지에서 목표와 까치 일러스트 저장 및 SNS 공유

 

 

2. user 페이지 (유저 정보 입력)

  • 정규식을 사용한 유효성 검사 

6개월 뒤에 이메일을 전송하기 때문에 유저가 이메일을 바르게 작성하도록 정규식을 사용해 이메일 유효성 검사를 했다. 일러스트 때문에 결과 페이지에서 나오는 카드의 비율이 정해져 있어 카드가 넘치지 않도록 닉네임도 8자를 초과하여 작성할 수 없도록 했다.

이메일 유효성 검사

 

 

 

  • 조건부 버튼 활성화

닉네임과 이메일 모두 필수 값이기 때문에 input 2개 모두 값이 옳게 들어왔을 때만 버튼이 활성화되도록 했다. 조금 더 많은 유저들이 메일 수신 동의를 할 수 있도록 메일 수신 동의 기본 값을 true로 주었다.

조건부 버튼 활성화

 

 

 

  • 기존 유저 확인 모달

로그인 기능이 없어 유저를 구분하는 PK로 이메일을 사용해 버튼을 누르면 서버와 통신해 기존 유저인지 확인한다. 기존 유저일 때만 계속 진행할지를 묻는 안내 모달을 띄운다.

기존 유저 안내 모달

 

 

 

3. write 페이지 (목표 적기)

  • Styled-component를 이용해 값마다 다르게 화면 렌더링

복 선택 페이지에서 어떤 복을 고르냐에 따라 까치 이미지와 까치의 한마디가 다르게 렌더링 되도록 했다. 까치 얼굴 div를 스타일드 컴포넌트로 만들어주고 fortune의 값에 따른 url을 props로 넘겨 배경이미지를 다르게 주었다.

까치의 한마디 div를 스타일드 컴포넌트로 만들고 넘겨준 props를 key로 활용해 styled-components theme에 있는 색으로 배경색을 넣어주었다.

...
// JSX
<section className='kkachi-container'>
  <h1 className='title-2 title'>까치와 올해 목표를 적어봐요</h1>
  <KkachiFace className='kkachi-face' $imgUrl={FORTUNELIST[getAssetUrl(fortune || '')]} />
  <KkachiTalk $fortuneColor={getFortuneColor(fortune)}>
    <h5 className='sub-title-2'>까치의 한마디</h5>
    <WriteSwiper />
  </KkachiTalk>
</section>

...

// Styled-components
const KkachiFace = styled.div`
  background: ${({ $imgUrl }: { $imgUrl: string }) => `center/100% no-repeat url(${$imgUrl})`};
`;

const KkachiTalk = styled.div<KkachiColorProps>`
  position: relative;
  width: 80%;
  margin: 0 20px 24px 20px;
  padding: 12px 20px 8px 20px;
  background-color: ${({ theme, $fortuneColor }) => theme.colors[$fortuneColor]};
  border-radius: 6px;
  text-align: center;
 ...`

선택한 복에 따라 달라지는 화면

 

 

 

  • Swiper 라이브러리를 이용한 슬라이드 구현

그동안 항상 슬라이드를 손수 구현하다가 슬라이드 라이브러리를 처음 사용해봤는데 속성만 넣어주면 무한 슬라이드, 자동 재생 슬라이드를 손쉽게 만들 수 있어 놀라웠다.

 

 

 

  • state를 이용한 목표 적기

목표를 state에 담아 추가 버튼을 누르면 id와 빈문자열을 content로 갖는 객체가 state에 추가되도록 했다. 첫 번째 목표는 삭제할 수 없도록 했고 목표가 5개가 되면 추가 버튼이 사라지도록 했다. 목표는 추가&삭제가 되므로 별도의 id를 state로 두어 key로 사용했다. 첫 번째 input에 목표가 적혀있을 때만 버튼이 활성화되도록 해 목표가 최소 1개 이상 적히도록 했다.

function Write({ name, email, goalList, setGoalList, consentCheck }: WriteProps) {
  const [unqId, setUnqId] = useState(2);

...

  const handleAddGoalList = () => {
    if (goalList.length >= 5) return;
    setGoalList((prev) => [...prev, { id: unqId, content: '', focus: false }]);
    setUnqId((prev) => prev + 1);
  };
  
...

새해 목표 적기 input

 

 

 

4. result 페이지 마크업 및 이미지 저장 기능

  • 카드 플립 css애니메이션 구현 및 엽서 비율 고정

클릭할 때마다 flip state : boolean가 반대가 되도록 하고 flip의 값에 따라 transfrom rotate 값을 변경해 카드 플립 애니메이션을 구현했다. 일러스트가 깨지지 않게 엽서 비율을 고정해달라는 디자이너님의 요청에 따라 height가 padding-top %로 잡히게 해 화면이 줄어들어도 카드 비율은 일정하도록 고정했다.

카드 플립 애니메이션 및 엽서 비율 고정

 

 

 

  • html-to-image를 사용한 이미지 저장 기능 구현

html-to-image 라이브러리를 사용해 element를 canvas 요소로 변경해 이미지 저장 기능을 구현했다. 처음에는 htmlToJpeg 함수로 앞, 뒷면 모두 다운로드했다. 그러나 카드 플립 애니메이션 구현 때문인지 앞면이 좌우반전 상태로 저장 돼 함수를 분리하고 까치 일러스트 이미지 파일을 다운로드하도록 변경했다.

import { toJpeg } from 'html-to-image';

export const htmlToJpeg = (ref: React.RefObject<HTMLDivElement>) => {
  if (ref.current === null) return;
  toJpeg(ref.current, { cacheBust: true })
    .then((dataUrl) => {
      const link = document.createElement('a');
      link.download = '올해까치_2023 MYGOALS';
      link.href = dataUrl;
      link.click();
    })
    .catch((err) => {
      console.log(err);
    });
};

export const saveKkachiImg = (url: string) => {
  if (!url) return;
  const link = document.createElement('a');
  link.download = '올해까치_kkachi of fortune';
  link.href = url;
  link.click();
};

 

 

 

해결해야 했던 문제

1. 개인 정보 수집과 보안

해킹

.env 파일을 누구나 볼 수 있는 레포에 올려둔 모 서비스를 봤다. 그걸 보며 나쁜 마음을 먹는다면 firebase에 접근해서 개인정보를 악용할 수 있다고 생각했고, 화제가 되었던 내트꾸 서비스도 디도스 공격을 받았던 기억이 있어 혹시 모를 상황에 대비해 많은 개인정보를 수집하지 않기로 했다. 팀원 모두 지금 서비스가 공격을 받는다면 대처할 능력이 부족하다고 판단해 이메일 외에는 추가적인 개인정보를 받지 않기로 했고 그래서 회원가입 기능을 만들지 않았다.

 

결과 화면 공유

그동안 많이 공유됐던 서비스들은 다른 사람이 나에게 메시지를 전달하거나 테스트 결과로 만들어둔 화면 중 하나를 공유하는 방식이었다. 하지만 우리 서비스는 개인마다 결과화면이 달라 결과 화면 공유를 어떻게 해야 될지에 대한 고민이 있었다. 각자가 적은 목표를 볼 수 있는 화면을 공유하려면 회원가입 기능이 필요했고 또 보안 문제와 이어졌다. 그래서 결과에 대한 공유는 이미지 저장 기능으로 대체하고 서비스 링크만 공유하기로 했다.

 

2. 이미지 저장

반대로 저장되는 이미지

이미지 저장 기능을 구현 완료하고 테스트 중 새해 목표 이미지는 보이는 데로 저장되는데 까치 이미지를 저장하면 좌우반전 상태로 저장되는 문제가 발생했다. 카드 플립 애니메이션을 구현하면서 사용한 transfrom rotate나 preserve-3d 때문일 거라고 추측했다. 그래서 보이는 화면을 저장하던 방식에서 이미지파일 자체를 저장하는 방식으로 변경했다. 다운로드 함수 하나를 공통으로 사용했는데 위에 이미지 저장 기능 부분의 코드처럼 카드 면마다 함수를 다르게 사용해 앞면은 까치 일러스트 이미지 자체를 저장하도록 했다.

 

인앱브라우저

보통 무언가를 공유할 때 카카오톡을 제일 많이 사용하는데 카카오톡에서 링크를 열면 카카오 인앱브라우저에서 열린다. 크롬 브라우저에서는 잘 작동하는데 인앱브라우저에서 테스트를 하던 중 이미지 저장이 버튼만 눌리고 작동하지 않는 것을 발견했다. 찾아보니 인앱브라우저에서 지원하지 않는 기능이 많은데 그 중 하나인 것 같았다. 이미지 저장이 메인 기능 중 하나기 때문에 어쩔 수 없이 인앱브라우저 탈출 코드를 넣어서 카카오톡이나 인스타그램 인앱브라우저 접속 시 기본 브라우저로 이동하도록 했다.

 

3. 사파리 브라우저

404 페이지

다른 프론트 팀원도 나도 크롬 브라우저로 개발을 진행했다. 웹 사파리에서도 별다른 문제가 없었는데 모바일 사파리에서 새로고침을 하면 404 페이지가 뜨는 걸 발견했다. beforeunload 이벤트를 걸어 페이지를 떠나려고 하면 데이터가 저장되지 않을 수 있다는 팝업을 띄웠는데 팝업도 뜨지 않았고 없는 페이지 일 때 /으로 라우팅 해준 것도 작동하지 않았다. 그래서 public에 _redirects 파일을 생성해서 메인으로 보내 문제를 해결했다.

/*    /index.html   200

 

배경이 날아가서 저장

인앱브라우저에서 이미지 저장이 안 되는 걸 해결하니 그다음은 사파리 브라우저의 이미지 저장이 똑바로 되지 않는 게 문제였다. 이미지를 저장하면 빈화면으로 들어오거나, 보더만 들어오거나, 배경이미지가 사라져 저장되는 일이 빈번했다. 역시나 크롬으로 개발을 했기 때문에 발견하지 못한 문제였다. 웹 사파리로 접속해도 같은 오류가 있었다. 크롬에서는 문제가 발생하지 않았기 때문에 사파리 브라우저의 문제라고 결론 내리게 되었다. 이런저런 가설을 세워서 실험해봤지만 결국 해결하지 못했다. 카드를 여러 번 뒤집고 저장하면 배경이미지가 함께 들어오는데 처음 저장하면 배경이 사라졌다. 페이지에 카드를 충분히 뒤집고 저장하라는 안내문구를 넣는 것 말고는 대처할 방법이 없었다. 원인은 아직도 불명확하고 사파리 브라우저가 useRef나 background-image를 처리하는 방식이 뭔가 다른가 추측할 뿐이다..🥲

 

 

 

배운 점과 느낀 점

규모가 큰 서비스에 대한 존경

이런 작은 서비스를 운영하는데도 UX적인 부분도 고민해야 했지만 기술적으로도 고민해야 할 게 많았다. 문제를 해결하기 위해서 베스트라고 생각되는 방법으로 문제를 해결하려면 기술이 뒷받침되어야 하는데 그 부분이 따라주지 못해서 아쉬웠다. 보안 문제로 골머리를 썩은 만큼 SSH를 사용하는 GitHub이나 많은 유저 정보를 안전하게 보관하는 규모가 큰 서비스들에 대해 존경심이 들었다. 큰 회사들이 왜 포지션별로 팀을 따로 두는 지도 알 것 같았다.

 

지식과 기술의 부재

하고 싶었던 기능들을 모두 구현하려면 그만큼 지식과 기술이 뒷받침이 되어야 한다는 부분을 뼈저리게 느꼈다..😭 기술이 뒷받침되어줬다면 회원가입 기능도 구현했을 테고 지식이 뒷받침되었다면 사파리 브라우저 이슈나 이미지 저장 기능에서 더 나은 방법으로 해결하거나 기획에서부터 알아챘을 거라고 생각한다..

서비스에 커스텀 도메인을 사용하려고 했는데 Netlify에서 커스텀 도메인을 사용하면 https를 위해 별도로 ssl 설정을 해줘야 했다. 그동안은 GitHub pages나 Netlify가 알아서 https로 배포해준 경험뿐이었기 때문에 당황스러웠다. 커스텀 도메인을 사용하기에는 배포일이 하루도 남지 않아 다음을 기약하기로 했다. 그동안신경 쓰지 않았는데 해킹이나 https와 같은 보안에 대한 지식이 너무 부족해 공부를 해야겠다는 생각이 들었다.

 

크로스 브라우징

그동안 크로스 브라우징을 동일한 화면을 띄우는 일로만 받아들이고 있었는데 기능적인 문제까지도 포함한다는 것을 배우게 되었다. 불편함 없이 아무렇지 않게 사용하던 인앱브라우저가 사실은 제멋대로인 환경이라는 점을 알게 되었고, 인앱브라우저에서는 지원하지 않는 기능들이 있다는 것을 포함해 외형만 달라보였던 브라우저들이 사실은 작동하는 방식이나 지원하는 기능들이 다르다는 것을 알게 되는 계기였다. 모바일을 크기가 작은 웹이라고 생각하고 있었는데 또 웹과는 다른 환경이라는 것도 느꼈다.

 

실패 요인

프로젝트 진행 중에 팀원들에게 틈만 나면 내 목표는 이용자 1000명이라고 이야기했었다. 아직 서비스 기한이 많이 남았지만 1000명을 도달할 수 있을지 자신이 없어졌다. 🥲 아무래도 생각하기에 실패 요인들을 꼽자면

  1. 남이 나에게 해주는 이야기가 아니라 혼자만의 이야기이기 때문에 많은 참여가 이뤄지지 않았다.
    • 생각보다 사람들은 남이 해주는 이야기를 좋아한다.
    • 자신의 목표를 남과 공유하고 싶어 하지 않는 사람들이 꽤 있다.
  2. 공유해서 주변 사람들과 이야기할 수 있는 화면이 없다.
    • 공유 화면으로 주변 사람들과 이야기를 할 수 없으니 공유가 활발히 이뤄지지 않았던 것 같다.
  3. 인앱브라우저 탈출 코드로 유저의 불편함이 추가되었다.
    • 유저들은 이 화면을 오류 화면이라고 생각한다.
    • 유저들에게 동작을 하나 더 수행하도록 해서 이탈률이 올라갔을 거라고 생각한다.
  4. 사전 홍보가 이뤄지지 않았다.
    • 일정도 짧고 급하게 진행된 프로젝트라서 4명의 인원으로 이 부분까지는 신경 쓰기 어려웠다.

정도로 볼 수 있을 것 같다. 예상보다 결과가 아쉽지만 이번의 경험을 발판 삼아 다음 서비스는 더 오류 없고 많은 이용자가 이용할 수 있는 서비스를 만들기 위해 노력해야겠다.

 

마무리

올해까치 프로젝트를 진행하면서 정말 많은 인사이트를 얻을 수 있었고 많이 배웠다. 유저가 직접 사용한다고 생각하고 서비스를 만드니 대충 넘어갔을 부분도 한 번 더 고민하게 되었고, 쉽게 생각하던 것도 사실은 어려웠던 스스로에게 부족함을 느끼면서 많이 성장할 수 있던 프로젝트였다. 아쉬운 점이 많지만 그래도 기간 안에 배포를 해냈다는 점이 잘했다고 생각한다.

유저가 직접 이용하는 서비스를 만드는 것은 역시 즐거운 일인 것 같다. 좀 더 많은 유저가 써줬으면 좋겠어서 다음 프로젝트 홍보를 위해 소통하고 홍보할 창구를 하나 만들어두어야겠다는 생각이 들었다.

그동안 살짝 건방졌던 것 같은데 겸손한 태도로 공부를 열심히 해야겠다. 갈 길이 멀다. 아자😀🔥

728x90
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함