-
Notifications
You must be signed in to change notification settings - Fork 0
๐งโ๐ซ 5์ฃผ์ฐจ ๋ฉํ ๋ง
FE
- ์๋ง์ ๋ฒ๊ทธ ํฝ์ค
- ๋ฆฌํฉํ ๋ง
- ์ปดํฌ๋ํธ ์ฌํ์ฉ
- hook โ module
- socket event๋ค์ npm ๋ฐฐํฌํด์ back/front ๊ณต์ ํด์ ์ฐ๋ ์๋
BE
- ๋ฒ๊ทธ ํฝ์ค ing..
- ๋์ปค, ๊นํ ์ก์ ๋ฑ๋ฑ
- ๋ฆฌํฉํ ๋ง
- ์ธํฐ์ ํฐ๋ฅผ ํ์ฉํ์ฌ ๋ก๊น ์ฒ๋ฆฌ + ํธ์คํธ ์๋ฒ์ ๋ก๊ทธ ์์ด๋๋ก ๊ตฌํ
- DTO๋ Entity ์์ฑ ์ static ๋ฉ์๋ ํ์ฉํ๋๋ก ์์
- Swagger API ๋ฐ์ฝ๋ ์ดํฐ โ ์ปค์คํ ๋ฐ์ฝ๋ ์ดํฐ ์์ฑํ์ฌ ์ ๋ฆฌ
- ๋ ํฌ์งํ ๋ฆฌ ์ ๋ฆฌ โ ๋ฐฑ์๋ ๋ฐฐํฌ ํ์ผ ๋ฐฑ์๋ ๋๋ ํ ๋ฆฌ ๋ด๋ถ๋ก ์ง์ด๋ฃ์
- graceful shutdown
๋ฉํ ๊ฐ ๋ฏธ๋ฆฌ ์์ ๋ค์ ์ง๋ฌธ์ ๋ณด๊ณ ์ฌ ์ ์๋๋ก ์ฌ์ ์ ์ค๋นํ์ฌ ๊ณต์ ํฉ๋๋ค.
๐โโ๏ธ FE
์ง๋ฌธ 1๏ธโฃ setState๋ก ๋ฐ๋ ์ํ๋ฅผ ํ์ฌ ๋ฐ๋ก ์ฌ์ฉํ์ง ๋ชปํด์ ๋ฏธ๋์ ์ํ๊ฐ ๋ฐ๋ ๋ ๋ก์ง์ ์ฒ๋ฆฌํด์ฃผ๋๋ก ํ๋๋ฐ ์ข์ ๋ฐฉ๋ฒ์ผ๊น์?
// useBlocker.tsx
export function useBlocker({ when, onConfirm, onCancel }: useBlockerParams) {
const [blockedGoBack, setBlockedGoBack] = useState<boolean>(true);
const [afterRunCallbacks, setAfterRunCallbacks] = useState<(() => void)[]>([]);
const { openExitPopup } = useExitPopup();
// true๋ฅผ ๋ฆฌํดํ ๊ฒฝ์ฐ ํ์ด์ง ์ด๋์ด ๋งํ
// false๋ฅผ ๋ฆฌํดํ ๊ฒฝ์ฐ ํ์ด์ง ์ด๋์ด ๊ฐ๋ฅ
reactRouterUserBlocker(args => {
if (!blockedGoBack) {
return false;
}
if (when({ ...args })) {
openExitPopup({
onConfirm: () => {
unblockGoBack(() => {
onConfirm?.();
});
},
onCancel: ({ close }) => {
onCancel?.();
close();
},
});
return true;
}
return false;
});
// blockedGoBack์ด ๋ฐ๋์์ ๋ afterRunCallbacks ๋ด์ฉ์ ๋ชจ๋ ์คํํด์ค
useEffect(() => {
if (!blockedGoBack) {
afterRunCallbacks.forEach(callback => {
callback();
});
setAfterRunCallbacks([]);
}
}, [blockedGoBack]);
const unblockGoBack = (afterRunCallback: () => void) => {
setBlockedGoBack(false);
setAfterRunCallbacks(prev => [...prev, afterRunCallback]);
};
return { blockedGoBack, unblockGoBack };
}
// unblockGoBack ์ฌ์ฉ
const { unblockGoBack } = useBlocker({...});
unblockGoBack(()=> {
// ํ์ด์ง ์ด๋ ๋ง๊ธฐ๋ฅผ ํด์ ํ๋ฉด์ ์ฝ๋ฐฑ ํจ์ ์คํ
});
์ง๋ฌธ 2๏ธโฃ ์ค๋ฒ๋ ์ด์ ํ์ธ
ํน์ ์ทจ์
๋ฒํผ์ ๋๋ ์ ๋ ์ค๋ฒ๋ ์ด๋ฅผ ๋ซ์์ง ํ๋จ์ ์ธ๋ถ์์ ํด์คฌ์ต๋๋ค. ๊ทธ๋์ ์ฝ๋ฐฑํจ์์ ์ฝ๋ฐฑํจ์์ ์ฝ๋ฐฑํจ์๋ฅผ ๋ฃ์ด์คฌ๋๋ฐ ์ด๋ ๊ฒ ์์ฑํ๋๊ฒ ์ ์ผ ์ข์ ๋ฐฉ๋ฒ์ผ๊น์?
// Popup.tsx
export default function Popup({ onCancel, onConfirm, children }: PopupProps) {
...
return (
...
<Button onClick={onCancel}> ์ทจ์ํ๊ธฐ </Button>
<Button onClick={onConfirm}> ํ์ธํ๊ธฐ </Button>
...
);
}
// PasswordPopup.tsx
export default function PasswordPopup({ onCancel, defaultValue, onSubmit }: PasswordPopupProps) {
...
return (
<Popup
onCancel={onCancel}
onConfirm={passwordSubmit}
>
...
</Popup>
);
}
// usePasswordPopup.tsx
export function usePasswordPopup() {
const { open } = useOverlay();
const openPasswordPopup = ({ host, onSubmit, onClose }: openPasswordPopupParams) => {
open(({ close }) => (
<PasswordPopup
onCancel={() => onClose?.({ close })}
onSubmit={ password => {
onSubmit?.({ password, close });
}}
/>
));
};
return { openPasswordPopup };
}
// usePasswordPopup ์ฌ์ฉ
const { openPasswordPopup } = usePasswordPopup();
const createRoom = ({ roomName, onSuccess, onClose }: createRoomParams) => {
openPasswordPopup({
onClose: () => onClose?.({ close }),
onSubmit: ({ password, close }) => {
socketManager.emit('createRoom', roomName, password);
socketManager.on('roomCreated', () => onSuccess?.({ password, close }));
},
});
};
// createRoom ์ฌ์ฉ
createRoom({
roomName: roomName as string,
onSuccess: ({ close }) => {
navigate('setting');
close();
enableSideBar();
},
onClose: ({ close }) => {
close();
navigate('/');
},
});
์ง๋ฌธ 3๏ธโฃ socket event type๋ค์ npm package๋ก ๋ฐฐํฌํด์ back/front ๊ณต์ ํด์ ์ฌ์ฉํ๋๋ก ํด๋ดค๋๋ฐ, ์ด๊ฒ ์ข์ ๋ฐฉ๋ฒ์ผ๊น์?
export type AIServerEvent = "tarotCard" | "chatEnd" | "streamStart" | "streaming" | "streamEnd";
export type AIClientEvent = "message" | "tarotRead";
๐โโ๏ธ BE
์ง๋ฌธ 1๏ธโฃ ๋์ปค๊ฐ ์ด์ ์์ ๋ด์ญ์ ๊ธฐ์ค์ผ๋ก ๋ฐฐํฌํ๋ ๊ฒ ๊ฐ์ต๋๋ค ๐ฑ
์ด๋ฒ ์ฃผ์ ๋์ปค๊ฐ ์ด์ ๋ฒ์ ์ผ๋ก ๋น๋๋๋ ๊ฒ ๊ฐ๋ค๊ณ ๋ง์๋๋ ธ๋๋ฐ, ํ์ธ ๊ฒฐ๊ณผ docker compose๋ docker์ ๋ฌ๋ฆฌ -t
์ต์
์ด ์๋ค๋ ๊ฑธ ์๊ฒ ๋์์ต๋๋ค. ๊ทธ๋์ ๋งค๋ฒ latest
ํ๊ทธ๊ฐ ๋ถ๊ณ ์์๋๋ฐ ์ค๋ฒ๋ผ์ด๋๋ ์๋ผ์ ์ฒ์ ์ด๋ฏธ์ง๊ฐ ๊ณ์ ์ฌ์ฉ๋๊ณ ์์์ต๋๋ค.
์ด๋ฏธ์ง ํ๊ทธ์ ๊นํ ํด์๋ฅผ ๋ฌ์ ๋ฒ์ ๊ด๋ฆฌ๋ฅผ ํ๋ ๊ฒ์ผ๋ก ๋ฐ๊ฟจ๋๋ฐ ์ฌ์ ํ ์ด์ ๋ฒ์ ์ผ๋ก ๋์๊ฐ๋ ๊ฒ ๊ฐ์ต๋๋ค.
- ๋ธ๋ฃจ ๋ฒ์
- was :
${DOCKER_USERNAME}/magicconch:was-blue-${GITHUB_SHA}
- signal :
${DOCKER_USERNAME}/magicconch:signal-blue-${GITHUB_SHA}
- was :
- ๊ทธ๋ฆฐ ๋ฒ์
- was :
${DOCKER_USERNAME}/magicconch:was-green-${GITHUB_SHA}
- signal :
${DOCKER_USERNAME}/magicconch:signal-green-${GITHUB_SHA}
- was :
์๋๋ docker images
๊ฒฐ๊ณผ์
๋๋ค.
REPOSITORY TAG IMAGE ID CREATED SIZE
kimyu0218/magicconch signal-blue-07fa3c66521a83d8f746fbf54fbbd34c15153ded 69f2ce6851a8 2 hours ago 1.32GB
kimyu0218/magicconch signal-blue-2f4b385b048a0b1e26d486c56eb6c2e98020c422 69f2ce6851a8 2 hours ago 1.32GB
kimyu0218/magicconch signal-blue-cff42b4c955b4ef80fe99a460ed75e6156dee104 69f2ce6851a8 2 hours ago 1.32GB
kimyu0218/magicconch signal-green-cff42b4c955b4ef80fe99a460ed75e6156dee104 69f2ce6851a8 2 hours ago 1.32GB
kimyu0218/magicconch signal-green-e4edd3dbd33462cf9b45eb3dd8e8cbd8863836af 69f2ce6851a8 2 hours ago 1.32GB
kimyu0218/magicconch was-blue-07fa3c66521a83d8f746fbf54fbbd34c15153ded 560d5152aec7 2 hours ago 1.43GB
kimyu0218/magicconch was-blue-2f4b385b048a0b1e26d486c56eb6c2e98020c422 560d5152aec7 2 hours ago 1.43GB
kimyu0218/magicconch was-blue-cff42b4c955b4ef80fe99a460ed75e6156dee104 560d5152aec7 2 hours ago 1.43GB
kimyu0218/magicconch was-green-cff42b4c955b4ef80fe99a460ed75e6156dee104 560d5152aec7 2 hours ago 1.43GB
kimyu0218/magicconch was-green-e4edd3dbd33462cf9b45eb3dd8e8cbd8863836af 560d5152aec7 2 hours ago 1.43GB
๋ฉํ ๋ง ์๊ฐ์ ๋๋ ์ด์ผ๊ธฐ๋ฅผ ๊ธฐ๋กํด๋ณด์ธ์.
-
๋์์ธ ํจํด ๊ตณ์ด ์ฑ ์ผ๋ก ๊ณต๋ถํ ํ์์๋ค.
- ํด๋ฆฐ ์ํคํ ์ฒ๊ฐ์ ์ฑ ์ ์ถ์ฒํ์ง ์๋๋ค. ์ง์ ๊ฒฝํ ํด๋ณด๋๊ฒ ์๋ฟ์
- ์ฝ๋๋ฅผ ๋ง์ด ์ง๊ณ ๊ด๋ฆฌ ํด๋ณด๋ฉด์ ๊ณ ํต์ ๋๋ผ๋ฉด์ ํจํด์ด ์๋ฟ๋๋ค ๐ฑ
-
๋ชจ๋ฐ์ผ ์น ๋ถ๋ถ ์ ํ๊ณผ ์ง์ค์ด ํ์ํ๋ค.
-
๋ค๋ก ๊ฐ๊ธฐ โ ๋ฆฌ์กํธ ๋ผ์ฐํฐ 5์๋ ์๊ณ 6์๋ ์์
- before unload
- useBlocker : ์ฝ๋ ๋ฆฌํฉํ ๋ง์ด ํ์ํด๋ณด์. ๋๋ฌด ๋ณต์กํจ
-
์ฝ๋ฐฑ์ ๋๊ฒจ์ ํ๋๊ฒ ๋ฌด๋ํ ๋ฐฉ๋ฒ
- ์ฝ๋ฐฑ์ ์ธ์๋ฅผ ๋๊ฒจ์ฃผ๋ ๊ฒ์ ์กฐ๊ธ ๊ทธ๋ ๋ค. onClose์ close๋ฅผ ๋๊ธฐ๋ ๊ฒ ์ด์ํ๋ค.
- ๋ ํ๋์ ๋ํํ๋ ํจ์๊ฐ ํ์ํ๋ค.
- ์ํ์ ๋ํด์ ์ธ๋ถ ์ธํฐํ์ด์ค๋ฅผ ์ฃผ์ ์ ํด์ฃผ๋ ๊ฒ ๋ซ๋ค. ์ฃผ์ ๋ฐฉํฅ์ด ์๋ฐฉํฅ์ด ๋๋ฒ๋ฆฌ๋ฉด ํ๋ค์ด์ง๋ค.
- open close๋ฅผ ์์์์ ๊ด๋ฆฌํ๋๋ก..
-
rootRouter ํ์ผ ์ด๋ฆ์ด ์์ฝ๋ค.
-
useTOLD
- ์ด๋ฆ์ ๋ฐ๊ฟ์ผ ํ๋ค. ๋ค๋ฅธ ์๋น์ค ์ฐ๋ฉด ๋ฐ๊ฟ์ผ ํ๋ค.
- ๋ช ์ฌ๋ ํน์ ๋๊ตฐ๊ฐ๋ฅผ ์ง์นญํ๋ฉด ์๋๋ค. ๋คํต์ ๋ง๋๋ค.
-
store/query โ get~ (X) use~
- query ์ด๋ฆ์ด๋ฉด, ๊ด๋ จ ์ํ๊ฐ๋ ๊ฐ์ด ๋ฐํ?
-
types ํ ๊ณณ์ ๋ชจ์๋๋ฉด ์ข๋ค โ types ํด๋ ์์
-
Socket ๋ถ๋ถ ์ฑ๊ธํค์ด ์๋๊ฒ๊ฐ๋ค.
-
์ปค์คํ ํ ์ ์ฌ๋ฌ๊ฐ ์ฐ๊ณ , depth๊ฐ ๊น์ผ๋ฉด ๋์ค์ ์คํ๋ ค ์ฝ๋๊ฐ ์ฝ๊ธฐ ์ด๋ ค์์ง ์ ์๋ค.
- ์คํ๋ ค ํ๊ณณ์ ๋ชจ์ฌ์๋๊ฒ ์ฝ๋๋ฅผ ์ฝ๊ธฐ ์ฌ์ธ์ง๋?
-
์ปค์คํ ํ ์ฌ์ฉํ๋ฉด ๋ฆฌํด ๊ฐ์ด ํด๋น ์ด๋ฆ๊ฐ์ด๋ฉด ์ข๊ฒ ๋ค.
- ex) useSubmit ์ด๋ฉด submit์ด ๋ฆฌํด๊ฐ!
์ด๋ฒ์ฃผ์ ์ฐ๋ฆฌ ํ์ด ๋๊ณ ์ถ์ ๋ชจ์ต์ ์์ํ๋ฉฐ ์ฒดํฌ๋ฆฌ์คํธ๋ฅผ ์ถ๊ฐํด๋ด๋ ์ข์ต๋๋ค. ๋ฉํ ๊ฐ ๋ณด๊ธฐ์ ์ฐ๋ฆฌ ํ์ ์ด๋ค์ง ์๊ฒฌ์ ๊ตฌํด๋ณด์ธ์.
- ์ฌ์ฉ์๊ฐ ์๋น์ค๋ฅผ ์ฌ์ฉํ ์ ์๋ ์์ค์ผ๋ก ์ฃผ์ ๊ธฐ๋ฅ์ด ๊ฐ๋ฐ๋์๋ค.
- GitHub ์ ์ฅ์๋ง ๋ด๋ ํ๋ก์ ํธ ๊ฐ์, ๊ธฐ์ ์ ๋์ , ๊ตฌํ ๊ณผ์ ์ ๋๊ตฌ๋ ์ ์ ์๋ค.
- ๋์ ์ฐ๋ฆฌ ํ์ ๊ธฐ์ ์ ์ธ ์๋ ๊ฑฐ๋ฆฌ๋ ๊ฐ์ ์ด ๋ฌด์์ธ์ง ๊ทธ ์ด์ ์ ํจ๊ป ์ค๋ช ํ ์ ์๋ค.
- 6์ฃผ์ฐจ์ ๋ฆฌํฉํ ๋ง ๋๋ ๊ฐ์ ํ ์์ญ์ด ๋ฌด์์ธ์ง ์ธ์งํ๊ณ ์๋ค.
๐ฎ ํ๋ก๋ฐํฌํฐ
๐ป ํ๋ก์ ํธ
- ์ผ ๋๋ ๋ฌด์ค๋จ ๋ฐฐํฌ ํ ์ ์์ด ๐ซต
- SWAGํ๊ฒ Swagger ์ฌ์ฉํ๋ ๋ฒ ๐ค (Feat. Swagger ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ํ ํจํค์ง ๋ง๋ค๊ธฐ)
- ์ธํฐ๋ํฐ๋ธ ์น: ์นด๋ ์ ๋๋ฉ์ด์
- ๐ป Context API, Provider ์ง์ฅ ๊ทธ๋ฆฌ๊ณ ๊ท์ฌ์ด ๊ณฐ๋์ด ๐ป
- ๐ ๋ณต์กํจ์ ๋จ์ํ๊ฒ, ๋ ๋์ ๊ฒฝํ์ผ๋ก ๐
- UX๋ฅผ ์ํด AI ์ฑํ ๋ฐ์ ์๋๋ฅผ 84.87%๊น์ง ๊ฐ์ ํ ๋ฐฉ๋ฒ ๐
- ์์ผ ์์ด๊ณ ๐ (Feat. socket.io๋ ํ์ฑ์ ํด)
- ๐ ์ ์ ํ์ผ public vs src/assets
- ๐ ์นด์นด์คํก ๊ณต์
- ๐ ์ธํฐ๋ํฐ๋ธ ์น ๊ตฌํ
- ๐ ๋ฐ์ํ ์น - ๋จ์๋ฅผ ์ ์ ํ๊ฒ ์ฌ์ฉํ๊ธฐ
- ๐ชฒ vite์์ tailwindcss ์ ์ฉ ์ด์
- ๐ชฒ tailwind์ ์ฅ๋จ์
- ๐ NCP ์ธํ๋ผ ๊ตฌ์ถํ๊ธฐ
- ๐ GitHub Actions๋ก CI/CD ์๋ํํ๊ธฐ
- ๐ SSL ์ธ์ฆ์ ๋ฐ๊ธ๋ฐ๊ธฐ
- ๐ ๋ธ๋ฃจ/๊ทธ๋ฆฐ ๋ฌด์ค๋จ ๋ฐฐํฌ ๊ตฌํํ๊ธฐ
- ๐ graceful shutdown์ผ๋ก ์ฌ์ฉ์ ๊ฒฝํ ์ ํ ๋ฐฉ์งํ๊ธฐ
- ๐ winston๊ณผ sentry๋ก ์๋ฒ๊ฐ ํฐ์ง๋ ์ด์ ๋ถ์ํ๊ธฐ (+ slack webhook)
- ๐ ์นด์นด์ค ๋ก๊ทธ์ธ ๋์ ํ๊ธฐ
- ๐ health check๋ก ์๋ฒ ์ํ ํ์ธํ๊ธฐ
- ๐ Jest์ Supertest๋ก ์ฝ๋ ๋์ ํ ์คํธํ๊ธฐ
- ๐ ๋์ปค ์บ์ฑ์ผ๋ก ๋น๋์๊ฐ 67% ๊ฐ์ ํ๊ธฐ
- ๐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ต์ ํํ๊ธฐ
- ๐ชฒ 521 Web server is down
- ๐ชฒ ๋์ปค ์ปจํ ์ด๋์์ ํธ์คํธ MySQL์ ์ ๊ทผ ๋ชปํ๋ ์ด์