๋ชฉ์ฐจ
Questine์ ๋ ๋ฒ์ ๊ณผ ๊ฐ์ RPG ์์๊ฐ ์ด์ง ๊ฐ๋ฏธ๋ผ ์๋ ํ ์ผ ๊ด๋ฆฌ ์๋น์ค์ ๋๋ค. ์ฌ์ฉ์๋ ๋งค์ผ ํ ์ผ์ ๊ธฐ๋กํ๊ณ , ํ ์ผ ๋ฌ์ฑ ์, ์บ๋ฆญํฐ๊ฐ ์ฑ์ฅํฉ๋๋ค. ์ฌ๋ฏธ์ ๋๊ธฐ๋ถ์ฌ๋ฅผ ๋์์ ์ ๊ณตํ๋ ์๋ก์ด ๊ฒฝํ์ ๋ชฉํ๋ก ํฉ๋๋ค.

![]() |
ํ ์ผ ์ถ๊ฐ ๋ฐ ์ญ์ ๋ฅผ ํ ์ ์์ต๋๋ค. ์๋ฃํ ํ ์ผ ๊ฐ์์ ๋ฐ๋ผ ๊ทธ๋ผ๋ฐ์ด์ ์์์ด ์ ์ฉ๋ฉ๋๋ค. |
์ ์
![]() |
๋ค์ํ ์
์ ์ ํ๋ํ ์ ์์ต๋๋ค. |
์บ๋ฆญํฐ
![]() |
๋ ๋ฒจ - ์๋ฃํ ํ ์ผ ๊ฐ์์ ๋ฐ๋ผ ๊ฒฝํ์น๋ฅผ ์ฌ๋ฆด ์ ์์ต๋๋ค. ์ด๋ฏธ์ง - ๋ ๋ฒจ์ ๋ฐ๋ผ ๋ค์ํ ์ด๋ฏธ์ง๋ฅผ ์ ๊ณตํฉ๋๋ค. |
๋ญํน
![]() |
๋ญํน - ์ ์ฒด ์ด์ฉ์์ ๋ญํน์ ํ์ธํ ์ ์์ต๋๋ค. |
ํ ์ผ ์ถ๊ฐ ๋ฐ ์ญ์ ์ ์ธํ ๋๋ ์ด ๋ฐ์ | |
---|---|
๐ง ์์ธ | ์๋ฒ ํต์ ๊ณผ์ ์์ ๋ฐ์ํ๋ ๋คํธ์ํฌ ์ง์ฐ์ผ๋ก ์ธํ ์ฌ์ฉ์ ์ฒด๊ฐ ์ฑ๋ฅ ์ ํ |
๐กย ํด๊ฒฐ | ๋๊ด์ ์ ๋ฐ์ดํธ๋ฅผ ๊ตฌํํ์ฌ ์ธํ ๋๋ ์ด ๊ฐ์ (React-query์ onMutate ํ์ฉ) |
// ํ ์ผ ์๋ฃ/๋ฏธ์๋ฃ ํ ๊ธ
export function useToggleTodoComplete(year?: number, month?: number) {
const queryYear = year ?? now.year();
const queryMonth = month ?? now.month() + 1;
const queryKey = getTodosQueryKey(queryYear, queryMonth);
const storageKey = getTodosStorageKey(queryYear, queryMonth);
return useMutation({
...
onMutate: async ({ todo_id, completed }) => {
// ํ ์ผ ๋ชฉ๋ก๊ณผ ๊ด๋ จ๋ ์์ฒญ์ด ์๋ค๋ฉด ์ทจ์ (์๋ฒ์์ ๋๊ธฐํ ์ถฉ๋ ๋ฐฉ์ง ๋ชฉ์ )
await queryClient.cancelQueries({ queryKey });
// ํ์ฌ ์ฟผ๋ฆฌ ์บ์์ ์ ์ฅ๋ ํ ์ผ ๋ชฉ๋ก ๋ถ๋ฌ์ด (๋ฐ์ดํฐ๊ฐ ์๋ค๋ฉด ๋น ๋ฐฐ์ด ๋ฐํ)
const previousTodos = queryClient.getQueryData<Todo[]>(queryKey) ?? [];
// ํ ์ผ ๋ชฉ๋ก์์ ์ฌ์ฉ์๊ฐ ํ ๊ธํ ๊ฐ ์
๋ฐ์ดํธ
const newTodos = previousTodos.map((todo) => (todo.todo_id === todo_id ?
{ ...todo, completed } : todo));
// ๋ณ๊ฒฝ๋ ํ ์ผ ๋ชฉ๋ก ์
๋ฐ์ดํธ (๋๊ด์ ์
๋ฐ์ดํธ)
queryClient.setQueryData<Todo[]>(queryKey, newTodos);
await saveTodosStorage(storageKey, newTodos);
// ์๋ฌ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ, ์ด์ ํ ์ผ ๋ชฉ๋ก ๋ฐํ (๋กค๋ฐฑ -> onError์์ ์ด ๋ฐํ๊ฐ ์ด์ฉ)
return { previousTodos };
},
...
});
}
ํ ์ผ ์ถ๊ฐ ์ ํ์์กด ์ด์ ๋ฐ์ ์) 6์ 1์ผ์ ๋ฑ๋กํ๋ฉด 5์ 30์ผ์ ์ถ๊ฐ ๋จ | |
---|---|
๐ง ์์ธ | new Date๋ ๋ก์ปฌ ์๊ฐ๋ ๊ธฐ์ค์ผ๋ก ์์ฑํ๋๋ฐ ๋ฐฑ์๋์์๋ UTC ๊ธฐ์ค์ผ๋ก ์์ฑํจ |
๐กย ํด๊ฒฐ | ํด๋ผ์ด์ธํธ์ ์๋ฒ์ ์๊ฐ๋๋ฅผ KST๋ก ํต์ผํ์ฌ ํด๊ฒฐ (dayjs ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ) |
// before
const startDate = new Date(Date.UTC(year, month - 1, 1, 0, 0, 0, 0));
const endDate = new Date(Date.UTC(year, month, 0, 23, 59, 59, 999));
// after
const startDate = dayjs(`${year}-${month}-01 00:00:00`).toDate();
const endDate = dayjs(`${year}-${month}-01 00:00:00`).endOf('month').toDate();
์บ๋ฆฐ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ปค์คํ ์ค, ํ์ ์๋ฌ ๋ฐ์ | |
---|---|
๐ง ์์ธ | ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ ๊ณตํ๋ props์ ํ์ ์ด any๋ก ์ ์๋์ด์์ด ์ด๋ค ํ๋ผ๋ฏธํฐ๊ฐ ์ค๋์ง ์ ์ ์์ (๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ด์) |
๐กย ํด๊ฒฐ | ๋ฐ๋ก ์ปค์คํ ํค๋ ์ปดํฌ๋ํธ๋ฅผ ๊ตฌํํ์ฌ import |
const customHeader = (props: CalendarHeaderProps) => {
const year = props.month?.getFullYear();
const month = props.month?.getMonth() + 1;
const today = dayjs();
const isCurrentMonth =
props.month && props.month.getFullYear() === today.year() && props.month.getMonth() === today.month();
// ์ ๋ณ๊ฒฝ ์ ํธ์ถํ ๊ณตํต ํจ์
const handleMonthChange = (monthOffset: number) => {
if (props.month && props.addMonth) {
const newDate = new Date(props.month);
newDate.setMonth(newDate.getMonth() + monthOffset);
props.addMonth(monthOffset);
// onMonthChange ์ธ๋ถ ์ํ ์
๋ฐ์ดํธ (๋๊ธฐํ)
onMonthChange({
year: newDate.getFullYear(),
month: newDate.getMonth() + 1,
day: 1,
timestamp: newDate.getTime(),
dateString: `${newDate.getFullYear()}-${String(newDate.getMonth() + 1).padStart(2, '0')}-01`,
});
}
};
<CalendarList
...
customHeader={customHeader}
...
/>
refresh token์ด ์ ํจํจ์๋ ๋ถ๊ตฌํ๊ณ access token ๋ง๋ฃ ์๊ฐ์ด ์ง๋๋ฉด ๋ก๊ทธ์์๋๋ ํ์ | |
---|---|
๐ง ์์ธ | access token ๋ง๋ฃ ์, ์๋ฒ์๊ฒ ์ฌ๋ฐ๊ธ ํ๋ ๊ณผ์ ์ ์๋ตํจ |
๐กย ํด๊ฒฐ | axios interceptor๋ฅผ ์ด์ฉํ์ฌ 401 (unauthorized) ์ผ ๊ฒฝ์ฐ token์ ์ฌ๋ฐ๊ธํ ์ ์๋๋ก ๋ณ๊ฒฝ |
axiosInstance.interceptors.request.use(async (config) => {
const accessToken = await getSecureStore('accessToken');
if (accessToken) {
if (config.headers) config.headers['Authorization'] = `Bearer ${accessToken}`;
}
return config;
});
axios.interceptors.response.use(
(response) => response,
async (error) => {
try {
if (error.response?.status === 401) {
await getAccessToken();
}
} catch (error) {}
}
);
app/
: ๋ผ์ฐํ
๋ฐ ์ฃผ์ ํ๋ฉด ์ปดํฌ๋ํธ (file-based routing)
tabs/
: ๋ฉ์ธ ํญ ํ๋ฉด (Achievement, Character, Profile, Rank ๋ฑ)
auth/
: ์ธ์ฆ ๊ด๋ จ ํ๋ฉด (Login, Register ๋ฑ)
settings/
: ์ค์ ๊ด๋ จ ํ๋ฉด
components/
: ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ UI ์ปดํฌ๋ํธ ๋ฐ ์
๋ ฅ ์ปดํฌ๋ํธ
constants/
: ์์ ๋ฐ ๊ณตํต ๊ฐ ์ ์ (์: Colors, Calendars)
hooks/
: ์ปค์คํ
ํ
(์: useAuth, useTodo)
api/
: API ํต์ ๋ฐ ์ฟผ๋ฆฌ ํด๋ผ์ด์ธํธ
utils/
: ์ ํธ๋ฆฌํฐ ํจ์
assets/
: ์ด๋ฏธ์ง, ํฐํธ ๋ฑ ์ ์ ๋ฆฌ์์ค
types/
: ํ์
์ ์