#Medicine
#๋ณต์ฉ์ด๋ ฅ๊ด๋ฆฌ
#๊ณ ๊ฐ๊ด๋ฆฌ
#ํธ์์๋ฆผ
์ฝ ๋จน์ ์๊ฐ์ ์๋์ผ๋ก ๊ด๋ฆฌํด ์ฃผ๋ ์๋น์ค, Medi Ready์ ๋๋ค!
์ฝ์ ์ธ์ ๋จน์๋์ง ๊ธฐ์ตํ์ง ๋ชปํด ๊ณ ๋ฏผํ๊ณ ๊ณ์๋์? ์ด์ ๋ Medi Ready๋ฅผ ํตํด ๋ณต์ฉ ์ด๋ ฅ์ ์์ฝ๊ฒ ๊ด๋ฆฌํด ๋ณด์ธ์. Medi Ready๋ ์๋์ผ๋ก ์๋ฆผ์ด ๋ฑ๋ก๋๊ธฐ ๋๋ฌธ์ ๊ฐ๋จํ๊ฒ ์ด์ฉํ์ค ์ ์์ด์.
- ๐ MEDI READY
- ๐ Contents
- ๐ Links
- ๐ Preview
- ๐จโโ๏ธ Motivation
- ๐ฅ Features
- ๐ฌ Getting Started
- ๐ฅ Tech Stacks
- ๐ค ์ฐ๋ฆฌ๋ ์ด๋ ๊ฒ ๊ฐ๋ฐํ์ต๋๋ค
RN ์ฑ์ ๋ฐฐํฌ๊ฐ ๋์ง ์์, Expo๋ฅผ ํตํด ๋ก์ปฌ์์ ์คํํด์ผ ํฉ๋๋ค.
checkin.mp4
- ํ์๊ฐ ์ฝ๊ตญ์ ๊ฐ์ ์ฒดํฌ์ธ์ ํฉ๋๋ค.
- ์ฝ์ฌ๊ฐ ํ์์ ์ฑ์ ๋ณต์ฝ์ง๋์ ์ฒ๋ฐฉ์ ์ ๋ฌํฉ๋๋ค.
- ํ์์ ์ฑ์ ์๋์ด ๋ฑ๋ก๋ฉ๋๋ค.
alarm.mp4
- ์ค์ ํ ์๊ฐ์ ํธ์ ์๋ฆผ์ด ์ธ๋ฆฝ๋๋ค.
- ๋ฐฑ๊ทธ๋ผ์ด๋ ํ๊ฒฝ์์๋ ํธ์ ์๋ฆผ์ด ์ ๋ฌ๋ฉ๋๋ค.
history.mp4
- ๋ณต์ฉ ์ด๋ ฅ์ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
- ์ฒ๋ฐฉ ๋ด์ญ์ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
- ์๋ ์๊ฐ์ ์ค์ ํ ์ ์์ต๋๋ค.
๋งค์ผ ์ฑ๊ฒจ ๋จน์ด์ผ ํ๋ ์ฝ์ ๋จน์๋์ง ์ ๋จน์๋์ง ๊ธฐ์ต์ด ๋์ง ์์ ๋๊ฐ ์ข ์ข ์์ต๋๋ค. ๊ทธ๋๋ง๋ค ์๋ฆผ ์ฑ์ ์ฐพ์๋ณด๊ณค ํ๋๋ฐ์. ๋๋ถ๋ถ์ ์ฑ๋ค์ ๋งค๋ฒ ์ง์ ์ฝ์ ๋ฑ๋กํด์ผ ํ๊ฑฐ๋ ๋ณต์ฉ ์ด๋ ฅ์ ๊ด๋ฆฌํ ์ ์๋ ๋จ์ ์ด ์์ด ์ ์ฌ์ฉํ์ง ์๊ฒ ๋์์ต๋๋ค. ๊ทธ๋ฐ๋ฐ, ๋ง์ฝ ์๋์ผ๋ก ์๋ฆผ์ด ์ค์ ๋๊ณ , ๋ ์ฝ ๋จน์ ์๊ฐ์ ์๋ฆผ๊น์ง ์ธ๋ฆฐ๋ค๋ฉด ์ ๋ง ํธ๋ฆฌํ์ง ์์๊น? ๋ผ๊ณ ์๊ฐํ๊ฒ ๋์ด ์ด ํ๋ก์ ํธ๋ฅผ ๊ธฐํํ๊ฒ ๋์์ต๋๋ค.
์ด๋ป๊ฒ ์๋์ผ๋ก ์๋ฆผ์ ๋ฑ๋กํ ์ ์์๊น ๊ณ ๋ฏผ์ ํ์๋๋ฐ์. ์ต๊ทผ ์ฝ๋ก๋๋ก ์ธํด ๋ชจ๋ ์ฅ์์์ QR ์ฒดํฌ์ธ์ ์ฌ์ฉํ๋ ๊ฒ์์ ์๊ฐ์ ์ป์ด, ํ์๊ฐ ์ฝ๊ตญ์์ QR ์ฒดํฌ์ธ์ ํ๋ฉด ์์คํ ์์ ๋ฑ๋ก๋๋๋ก ํ๋ก์ ํธ๋ฅผ ์งํํ์ต๋๋ค.
๋ํ, ์ฝ๊ตญ ๊ณ์ ์ ์ฒ๋ฐฉ ๋ด์ญ์ด ์ถ์ ๋๊ธฐ ๋๋ฌธ์ ์ฝ๊ตญ ์ ์ฅ์์๋ ๊ณ ๊ฐ์ ๊ด๋ฆฌํ๋ ๋์์ ์ฝ ๋จน์ ์๊ฐ์ ์๋ ค์ฃผ๋ ์๋น์ค๋ฅผ ์ ๊ณตํ ์ ์๋ค๋ ์ธก๋ฉด์์ ๋งค๋ ฅ์ ์ด๋ผ๊ณ ๋๊ผ์ต๋๋ค.
- ํ์๋ QR ์ฒดํฌ์ธ์ ํตํด, ๋๊ธฐ ๋ช ๋จ์ ์ด๋ฆ์ ์ฌ๋ฆด ์ ์์ต๋๋ค.
- ์ฝ์ฌ๋ ์ฒ๋ฐฉ์ ๊ณผ ๋ณต์ฝ ์ง๋๋ฅผ ํ์์ ์ฑ์ผ๋ก ์ ์กํฉ๋๋ค.
- ํ์์ ์ฑ์ ์ฝ ์๋ฆผ์ด ๋ฑ๋ก๋ฉ๋๋ค. ex) 3์ผ๊ฐ ์์นจ, ์ ์ฌ, ์ ๋
- ๊ณผ๊ฑฐ ์ฒ๋ฐฉ ์ด๋ ฅ์ ํ์ธํ ์ ์์ต๋๋ค.
- ๋ ์ง๋ณ/์๊ฐ๋ณ๋ก ๋ณต์ฉ ์ฌ๋ถ๋ฅผ ์ฒดํฌํ ์ ์์ต๋๋ค.
- ์ค์ ํ ์๊ฐ์ ํธ์ ์๋ฆผ์ ๋ฐ์ต๋๋ค.
- ์๋์ ์ผ๊ณ ๋ ์ ์์ผ๋ฉฐ, ์๋ ์ด๋ ฅ์ ํ์ธํ ์ ์์ต๋๋ค.
- ์ง๋ ์ฒ๋ฐฉ ์ด๋ ฅ์ ํ์ธํ ์ ์์ผ๋ฉฐ, ์ฝ ์ ๋ณด๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
Local ํ๊ฒฝ์์ ์คํ ์ ์๋์ ๊ฐ์ด ํ๊ฒฝ ๋ณ์๋ฅผ ์ค์ ํด์ผ ํฉ๋๋ค.
Client (Web)
.env ํ์ผ์ ์๋์ ๊ฐ์ด ํ๊ฒฝ ๋ณ์๋ฅผ ์ ๋ ฅํด ์ฃผ์ธ์.REACT_APP_API_KEY=<Firebase API Key>
REACT_APP_AUTH_DOMAIN=<Firebase Auth Domain>
REACT_APP_PROJECT_ID=<Firebase Project ID>
REACT_APP_APP_ID=<Firebase App ID>
REACT_APP_BASE_URL=<default = http://localhost:8080>
Client (React Native Mobile App)
environment.js ํ์ผ์ ์๋์ ๊ฐ์ด ํ๊ฒฝ ๋ณ์๋ฅผ ์ ๋ ฅํด ์ฃผ์ธ์.import Constants from "expo-constants";
const ENV = {
dev: {
IOS_CLIENT_ID: <Google OAuth IOS client ID>,
ANDROID_CLIENT_ID: <Google OAuth Android client ID>,
API_SERVER_URL: <YOUR_IP_ADDRESS_WITH_PROT>,
},
};
const getEnvVars = (env = Constants.manifest.releaseChannel) => {
if (__DEV__) {
return ENV.dev;
}
};
export default getEnvVars;
Main Server
.env ํ์ผ์ ์๋์ ๊ฐ์ด ํ๊ฒฝ ๋ณ์๋ฅผ ์ ๋ ฅํด ์ฃผ์ธ์.ORIGIN_URI_DEV=<origin uri: default = "http://localhost:3000">
PUSH_NOTIFICATION_URI=<origin uri: default = http://localhost:8081>
MYSQL_USERNAME=<mysql username: default = root>
MYSQL_PASSWORD=<mysql password>
MYSQL_DATABASE=mediready
MYSQL_HOST=<my sql host: default = "127.0.0.1">
JWT_SECRET_KEY=<jwt secret key>
COOKIE_SECRET_KEY=<cookie secret key>
Push Notification Server
.env ํ์ผ์ ์๋์ ๊ฐ์ด ํ๊ฒฝ ๋ณ์๋ฅผ ์ ๋ ฅํด ์ฃผ์ธ์.EXPO_ACCESS_TOKEN=<Expo Access Token>
- React
- React Native (Expo)
- Styled-Component
- Redux Toolkit
- Redux Saga
- React Query
- Node JS
- Express
- MySQL, Sequelize
- AWS RDS
- Node Schedule
- Expo Server SDK
- Jest
- React Testing Library
- Mocha, Chai
Test Case Coverage Client 54%, React Native 46%, Server 73%
- Netlify
- AWS Elastic Beanstalk
ํ ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉด์ ํจ์จ์ ์ธ ํ์ ๋ฐฉ์๊ณผ ๊ฐ์ ๋งก์ ๋ถ๋ถ์ ๋์์ธ์ ํต์ผ์ฑ์ ์ํด ์ปดํฌ๋ํธ ๊ตฌ์กฐ๋ฅผ ์ค๊ณํ์ต๋๋ค. ํนํ, ์ปดํฌ๋ํธ๋ฅผ ์ผ๋ฐํํ ๋ค ํฉ์ฑ(Composition)ํด์ ์ฌ์ฉํ ์ ์๋๋ก ํ๊ณ , ํ ๊ณณ์์๋ง ์ฌ์ฉ๋๋ ์ปดํฌ๋ํธ๋ผ๋ ์ฌ์ฌ์ฉ์ฑ์ ๊ณ ๋ คํ์ฌ ๋ถ๋ฆฌํ์ต๋๋ค. ๋ํ Proptypes๋ฅผ ์ค์ ํ์ฌ ์ปดํฌ๋ํธ์ ์์ฑ์ ์ฝ๊ฒ ํ์ ํ ์ ์๋๋ก ํ์ต๋๋ค.
์ด๋ก ์ธํด ์์ ์๊ฐ์ ๋จ์ถํ๊ณ , ๋ถํ์ํ ์ปค๋ฎค๋์ผ์ด์ ๊ณผ์ ์ ์ค์ผ ์ ์์์ผ๋ฉฐ, ์ ๋ถ๋ฆฌ๋ ์ปดํฌ๋ํธ ๋๋ถ์ ์์ํ๊ฒ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์์ต๋๋ค. ๋ํ ๋ฆฌ์กํธ์ ์ธ ์ฌ๊ณ ๋ฐฉ์์ ์ค์์ฑ๊ณผ ์ปดํฌ๋ํธ๋ฅผ ์์ํ๊ฒ ์์ฑํ๋ ๊ฒ์ ์ฅ์ ์ ๋ํด ๋๋ ์ ์์์ต๋๋ค.
๋ธ๋ผ์ฐ์ ์์ ์คํ๋๋ React์๋ ๋ค๋ฅด๊ฒ, ๋ชจ๋ฐ์ผ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์คํ๋๋ React native๋ ๋ค๋ฅธ ํ๋ฉด์ผ๋ก ์ด๋ํ์ ๋ component๊ฐ unmount ๋์ง ์๊ณ stack ๊ตฌ์กฐ๋ก ์์ด๊ธฐ ๋๋ฌธ์ life cycle์ ์ด์ฉํ์ฌ ์ ์ญ ์ํ๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐ ์ด๋ ค์์ ๊ฒช์์ต๋๋ค. ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด React Navigation์ "focus" ์ด๋ฒคํธ๋ฅผ ์ฌ์ฉํ์ฌ ์ํ๋ฅผ ์ ๋ฐ์ดํธํ๋ ๋ฐฉ์์ ์ฌ์ฉํ์์ต๋๋ค.
navigation.addListener("focus", () => {
dispatch(...);
});
์ด๋ฒ ํ๋ก์ ํธ์์ React Native๋ฅผ ์ฒ์ ์ ํ๊ธฐ ๋๋ฌธ์, ํ๋ก์ ํธ ๊ธฐ๊ฐ์ ๊ณ ๋ คํ์ฌ Expo๋ฅผ ์ฌ์ฉํ์ต๋๋ค. ๊ทธ๋ฌ๋ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ํ ๋๋ฉด ์ข ์ข ์๋ฌ๊ฐ ๋ฐ์ํ๊ณ , ํธ๋ค๋ง์ด ์ฝ์ง ์์์ต๋๋ค. ๊ทธ๋์ ๋น๋๊ธฐ ๋ฐ ์ฌ์ด๋ ์ดํํธ ๊ด๋ฆฌ์ ์ญํ ์ ๋ฐ๋ฅธ ์ฝ๋ ๋ถํ ์ด ์ฉ์ดํ Redux-Saga๋ฅผ ๋์ ํ๊ธฐ๋ก ๊ฒฐ์ ํ์ต๋๋ค.
Generator์ ๊ฐ์ ์๋ก์ด ๊ฐ๋ ๋ค์ ์ดํดํ๊ณ ์ ์ฉํ๋ ๋ฐ ์๊ฐ์ด ์์๋์์ง๋ง, ๊ฒฐ๊ณผ์ ์ผ๋ก ํจ์จ์ ์ธ ์๋ฌ ํธ๋ค๋ง์ด ๊ฐ๋ฅํ๋ค๋ ์ ์ ๋๊ผ์ต๋๋ค. ํนํ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ๋จ์ํ ๊ธฐ๋ค๋ฆฌ๋ ๊ฒ์ด ์๋๋ผ, ์ฃผ๋์ ์ผ๋ก ์ก์ ์ ๋ชจ๋ํฐ๋งํ๊ณ ์ปจํธ๋กคํ ์ ์๋ค๋ ์ ์์ Redux-Saga์ ์ฅ์ ์ ๋๋ ์ ์์์ต๋๋ค.
๋ค๋ง, Toolkit๊ณผ Redux-Saga๋ฅผ ๋ถ๋ฆฌํ์ฌ ํ์ผ ๊ตฌ์กฐ๋ฅผ ์ค๊ณํ๋ค๋ ์ ์์ ์์ฌ์์ด ๋จ์ต๋๋ค. ๋ค์๋ฒ์๋ Slice์ Redux-Saga๊ฐ ํ๋์ ํ์ผ์ด ํ ๊ฐ์ง ๊ธฐ๋ฅ์ ๋ด๋นํ๋ Ducks ํจํด์ ์ด์ฉํด ๋์ฑ ์ง๊ด์ ์ธ ๊ตฌ์กฐ๋ฅผ ์ค๊ณํด ๋ณด๊ณ ์ถ์ต๋๋ค.
์์
// What I did
src/
api
component
redux/
reducers
store
sagas/
handlers
rootSaga.js
// What I should have done
src/
api
component
feature/
index.js
slice.js
saga.js
์ด๊ธฐ์๋ Firebase Cloud Messaging๊ณผ ์ง์ ํต์ ํ์ฌ ํธ์ ์๋ฆผ ๊ธฐ๋ฅ์ ๊ตฌํํ๋ ค๊ณ ๊ธฐํํ์ต๋๋ค. ๊ทธ๋ฌ๋ ํด๋น ๋ฐฉ์์ ์๋๋ก์ด๋/IOS๋ฅผ ๊ตฌ๋ถํ์ฌ ๊ฐ๋ฐํด์ผ ํ๋ค๋ ์ ๊ณผ ๋น์ฉ์ ์ง๋ถํด์ผ ํ๋ค๋ ๋จ์ ์ด ์์ด Expo SDK ์๋ฒ๋ฅผ ํตํด ์ฐํ์ ์ผ๋ก FCM์ ์ด์ฉํ๋ ๋ฐฉ์์ ๋์ ํ์ต๋๋ค.
ํ์ง๋ง, FCM ์๋ฒ์ ์ง์ ํต์ ํ์ง ์๋ ํ์ ์ค์ ํ ์๋ฆผ์ ๊ด๋ฆฌํ๊ธฐ๊ฐ ์ด๋ ต๋ค๋ ์ ์ด ์์ฝ๊ฒ ๋๊ปด์ง๋๋ค. ํ์ฌ๋ก์๋ ์๋ฆผ์ ์ํ ๋ณ๋ ์๋ฒ๋ฅผ ๊ตฌ์ถํ์์ง๋ง, DB์ ์๋ฆผ ์ ๋ณด๋ฅผ ๋ชจ๋ ์ ์ฅํ ๋ค Node schedule๊ณผ Expo SDK ์๋ฒ๋ฅผ ํตํด ์ฐํ์ ์ผ๋ก ์๋ฆผ์ ๊ด๋ฆฌํด์ผ ํ๋ ๋นํจ์จ์ ์ธ ๊ตฌ์กฐ๋ฅผ ๊ฐ๊ณ ์์ต๋๋ค. ์ถํ์๋ ์ด๋ฌํ ์ ์ ๊ฐ์ ํ์ฌ ์๋ฆผ ์๋น์ค๋ฅผ ๋์ฑ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํด ๋ณด๊ณ ์ถ์ ๋ง์์ ๋๋ค.
CRM ๋ชฉ์ ์ ๋ถํฉํ๊ธฐ ์ํด, ํ์ ์ ๋ณด์ ๊ธฐ๋ฐํ์ฌ ๊ฒ์์ด๋ฅผ ์ถ์ฒํ๋ ์๊ณ ๋ฆฌ์ฆ์ ๊ตฌํํ๋ ค๊ณ ์๋ํ์ต๋๋ค. ๊ทธ๋ฌ๋ ์ํ์ ์ธ ์ง์๊ณผ ๋ง์ ๋ฐ์ดํฐ๊ฐ ํ์ํ๋ค๋ ์ ์์ ํ๊ณ๊ฐ ์๋ค๊ณ ํ๋จํ์ฌ ๋ค๋ฅธ ๋ฐฉ์์ ์ฐพ์๋ณด์์ต๋๋ค. ๊ฒฐ๊ณผ์ ์ผ๋ก, ํ์ฌ CRM ์์์ ํค์๋์ ๊ฒ์ ๋น๋์์ ๊ธฐ๋ฐํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ ๊ณตํ๊ฒ ๋์์ต๋๋ค.
DB Modeling ๊ณผ์ ์์ ๋ณต์กํ ๊ด๊ณ๊ฐ ์์๋์ด ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฒ์์ผ๋ก ๋ค๋ฃจ์ด ๋ณด์์ต๋๋ค. NoSQL์ Nested ํํ์ ์ต์ํ ํ์, ์ฌ๊ณ ๋ฅผ ์ ํํ๋๋ฐ ์๊ฐ์ด ๋ง์ด ์์๋์์ต๋๋ค. ํ์ง๋ง ๋ณต์กํ ๊ด๊ณ๋ฅผ ์ฐ๊ฒฐ ์ง๊ณ , Join๊ณผ Cascade๋ฅผ ์ด์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๋ฉด์ SQL์ ์ฅ์ ์ ๋๋ผ๊ฒ ๋์์ต๋๋ค. ํนํ ๋ณต์กํ ๊ด๊ณ ๋๋ฌธ์ ๋ฐ์ดํฐ๋ฅผ ์๋ชป ์ ๋ ฅํ ๊ฒฝ์ฐ๊ฐ ์์๋๋ฐ, ๊ทธ๋๋ง๋ค ์ฌ๋ฐ๋ฅธ ๋ฐ์ดํฐ ๊ตฌ์กฐ๊ฐ ์๋๋ผ๋ ์ค๋ฅ ๋ฉ์์ง๋ก ์ธํด ์ฝ๊ฒ ์ค๋ฅ๋ฅผ ์ก์๋ผ ์ ์์์ต๋๋ค. ์์ง์ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์ต์ํ์ง ์์ ํ์, ์ฟผ๋ฆฌ๋ฌธ ์ต์ ํ๋ฅผ ์๋ํ์ง ๋ชปํ๋๋ฐ, ์ถํ์ ๊ผญ ํ๋ฒ ์๋ํด ๋ณด๊ณ ์ถ์ต๋๋ค.