소중한 일상을 공유해보세요 /released 240225
- 다양한 일기 작성 탬플릿과 특별한 기능으로, 더 심플하게 일기를 쓸 수 있어요.
- 회원 등급에 따라 차별된 서비스를 제공하며, 공유 일기장을 통해 친구들과 소통하세요.
- 기분 스티커, 댓글, 좋아요 기능을 통해 더 풍성한 일기 작성 경험을 제공해요.
- 모바일웹을 통해 언제 어디서든 편리하게 이용 가능합니다.
함께 쓰는 일기장으로 더 특별한 순간을 기록해보세요🙌
- 총 6명
| 역할 | 이름 | 연락처 | 비고 |
|---|---|---|---|
| 📝 PM | 이세은 | wntkfkd95@naver.com | 팀장 |
| 🎨 DE | 유수 | - | - |
| ⚙ BE | 권동휘 | hocci0222@kakao.com | BE 리드 |
| ⚙ BE | 김범준 | - | - |
| 📺 FE | 김주현 | sang.pok.e@gmail.com | FE 리드 |
| 📺 FE | 이어진 | - | - |
- 총 4달, 2023년 11월 02일 ~ 2023년 02월 25일
- 기존 팀원: 이세은, 유수, 권동휘, 김범준
- 24년 01월 01일 이후 합류 팀원: 김주현, 이어진
- 총 3일, 2024년 03월 01일 ~ 3일
- 총 2명
| 역할 | 이름 | 연락처 | 비고 |
|---|---|---|---|
| ⚙ BE | 권동휘 | hocci0222@kakao.com | - |
| 📺 FE | 김주현 | sang.pok.e@gmail.com | - |
- https://dassda.today, by Vercel
React, Typescript
- 2차 배포 목표가 PWA이기 때문에 하나의 어플리케이션으로 느끼게 만들고 싶어 React UI Library를 사용하였습니다.
Module SCSS
- CSS in JS는 스타일을 직렬화하는 과정에서 런타임 오버헤드가 걸리고 번들 크기를 늘리는 단점이 있습니다.
- 초기 로딩을 최대한 앞당기기 위함과, 스코프 지정 스타일을 사용하기 위해 Module SCSS를 선택했습니다.
React Query, Recoil
- 서버 사이드 상태를 효율적으로 캐싱하기 위해 React Query를 사용하였습니다.
- Header Config 등 클라이언트 상태를 효율적으로 관리하기 위해 Recoil을 사용하였습니다.
Axios
- Axios에서 제공하는 많은 편리성, 예를 들면 헤더 설정 및 Multipart 전송 등이 개발 경험을 좋게 만들어주어 선택하였습니다.
Jira: 팀 전체 진도 현황을 공유하였습니다.
Figma: 서비스 화면 및 기능에 대한 의견을 주고 받았습니다.
Slack: 주 소통 채널로서 팀원 또는 팀끼리의 협업에 대한 이야기를 나누거나 자료를 공유하였습니다.
Github Action을 통한 티켓 넘버 관리
- Github Action을 작성하여 Github Issue를 작성 시, 자동으로 Jira에 이슈를 생성합니다.
- 생성한 Jira 이슈의 티켓 넘버를 받아와 develop에서 branch를 따와 생성합니다.
Husky를 통한 커밋 컨벤션 관리
- 위의 상황과 마찬가지로, Commit 내용에도 해당 티켓 넘버가 포함되어 있어야 연동이 됩니다.
- Github Hook을 편하게 사용할 수 있게 제공해주는 Husky를 통해 커밋 시 자동으로 티켓 넘버가 기입되게 만들었습니다.
- Oauth를 통한 간편한 로그인을 제공하였습니다.
- 서비스 내에 로그인을 필요로 하는 Entry Point가 두 군데 존재하였습니다.
- Access Token을 받아오는 콜백 주소는 고정하되, localStorage를 통해 다음 서비스 경로를 지정해주는 흐름으로 구현하였습니다.
- 사용자 인증 방식은 JWT 방식을 사용하였습니다.
| 쓰다 로그인 흐름 순서도 |
|---|
![]() |
- 서비스를 이용하면서 놓칠 수 있는 부분을 알림으로 알려주고 있습니다.
- 총 5종류로 알림을 구분하였고, 각 종류별 마다 적절한 경로로 따라가게 했습니다.
React Query의useSuspenseInfiniteQueryHook을 사용하여 무한 스크롤 패칭을 구현하였습니다.useInfiniteObserver라는 Custom Hook을 만들어 스크롤 위치를 감지하였습니다.MutationObserver,Intersection Observerinterface를 사용하여 구현하였습니다.- 해당 방식은 쓰다 서비스의 무한 스크롤 방식의 모든 목록에 적용되었습니다.
| 쓰다 알림 화면 | 알림 종류 명세 |
|---|---|
![]() |
![]() |
- 상대방을 초대하기 위해선 공유 링크를 생성해야 합니다.
- 서버와 API 통신을 통해 해당 일기장 초대에 대한 해시값을 받아 링크를 생성합니다.
ClipboardAPI를 통해 복사하거나, 카카오톡 SDK에서 제공하는 공유하기 기능을 통해 초대를 보냅니다.- 초대 링크 화면에서는 해시값을 조회하고 유효한 해시인지 판별합니다.
- 사용자가 참여하기 액션을 하면 일기장 참여 핸들러 경로로 이동하여 참여를 시도합니다.
- location에 state를 담아 핸들러로 보냅니다. 직접 경로로 들어오는 경우 state가 없으므로 유효하지 않은 접근입니다.
| 쓰다 초대하기 모달 | 카카오톡 공유하기 | 공유 링크 접속 화면 |
|---|---|---|
![]() |
![]() |
![]() |
| * Network Throttle이 적용된 화면입니다 |
| 쓰다 초대하기 흐름 순서도 |
|---|
![]() |
- Skeleton UI를 적용하여 사용자에게 데이터 패칭 중임을 알려주고, 앞으로 나올 콘텐츠의 위치를 미리 알려주어 균일감 있는 경험을 제공합니다.
Suspense와Error Boundary를 활용하여 오류 화면과 로딩 화면을 비즈니스 로직에서 분리하였습니다.React Query의QueryErrorResetBoundary를 활용하여 재시도를 제공하였습니다.QueryErrorResetBoundary,Error Boundary,Suspense를 한 번에 처리하도록AsyncBoundaryProvider를 사용하였습니다.
| 일기 스켈레톤 | 오류 화면 대응 |
|---|---|
![]() |
![]() |
- Recoil을 통해 모달 정보를 담은 전역 배열을 만들었고, ModalController를 통해 렌더링합니다.
- 필요한 모달을 Component Modal, Confirm, Alert, Sheet로 구분하여 관리하였습니다.
- prop으로 필요한 정보를 넘겨줄 수 있도록 만들었습니다.
- 모달의 기본 기능을 제공함으로써 모달 콘텐츠는 본인의 역할에만 집중할 수 있도록 했습니다.
| ModalController
const ModalController = () => {
const { modals } = useModal();
const getTypedComponent = ({ id, type, isOpened, payload }: Modal) => {
if (type === 'Component') {
return <ComponentModal key={id} id={id} isOpened={isOpened} {...(payload as ComponentPayload)} />;
}
if (type === 'Alert') {
return <Alert key={id} id={id} isOpened={isOpened} {...(payload as AlertPayload)} />;
}
if (type === 'Confirm') {
return <Confirm key={id} id={id} isOpened={isOpened} {...(payload as ConfirmPayload)} />;
}
};
return modals.map(getTypedComponent);
};| openModal 코드
const openModal = useRecoilCallback(
({ set }) =>
({ type, payload }: { type: ModalType; payload: ModalPayloadType }) => {
const newId = uuid();
set(ModalStore, (prevState) => {
return [
...prevState,
{
id: newId,
type,
payload,
isOpened: true,
} as Modal,
];
});
return newId;
},
[],
);| openComponentModal Usage
openComponentModal<CreateShareLinkModalProps>({
title: '초대하기',
children: InviteMemberModal,
props: { board },
});








