Skip to content

Latest commit

Β 

History

History
367 lines (255 loc) Β· 8.56 KB

File metadata and controls

367 lines (255 loc) Β· 8.56 KB

Contributing Guidelines

FortuneMon ν”„λ‘œμ νŠΈμ— κΈ°μ—¬ν•΄μ£Όμ…”μ„œ κ°μ‚¬ν•©λ‹ˆλ‹€. 이 λ¬Έμ„œλŠ” 개발 μ‹œ μ§€μΌœμ•Ό ν•  κ·œμΉ™λ“€μ„ μ„€λͺ…ν•©λ‹ˆλ‹€. ν˜‘μ—…μ˜ μ›ν™œν•¨μ„ μœ„ν•΄ λ°˜λ“œμ‹œ μ•„λž˜ μ½”λ”© κ°€μ΄λ“œ 및 κ·œμΉ™λ“€μ„ λ”°λΌμ£Όμ„Έμš”.

BackEnd μ½”λ”© κ°€μ΄λ“œ

πŸ“Œ [1] Swagger / OpenAPI λ¬Έμ„œ 동기화

  • λ°±μ—”λ“œ API λ¬Έμ„œ(Swagger/OpenAPI)와 항상 λ™κΈ°ν™”λœ μƒνƒœλ‘œ APIλ₯Ό 개발 및 μ‚¬μš©ν•©λ‹ˆλ‹€.
  • μ‹€μ œ μ‚¬μš©ν•˜λŠ” API μš”μ²­ 경둜, 응닡 νƒ€μž… 등을 Swagger와 μΌμΉ˜μ‹œμΌœ μœ μ§€ν•©λ‹ˆλ‹€.

πŸ“Œ [2] μΌκ΄€λœ μ˜ˆμ™Έ 처리 ꡬ쑰

  • 곡톡적인 μ˜ˆμ™Έ 처리λ₯Ό μœ„ν•œ μ»€μŠ€ν…€ μ˜ˆμ™Έ 클래슀 및 곡톡 Response ν˜•μ‹μ„ μ‚¬μš©ν•©λ‹ˆλ‹€.
  • μ˜ˆμƒ κ°€λŠ₯ν•œ μ˜ˆμ™ΈλŠ” 미리 μ •μ˜λœ ν˜•μ‹μœΌλ‘œ ν”„λ‘ νŠΈμ— μ „λ‹¬λ˜λ„λ‘ μ²˜λ¦¬ν•©λ‹ˆλ‹€.

πŸ“Œ [3] ν™˜κ²½ λ³€μˆ˜ 관리

  • ν™˜κ²½ λ³€μˆ˜λŠ” .env λ˜λŠ” secrets λ“±μœΌλ‘œ κ΄€λ¦¬ν•˜λ©°, μ ˆλŒ€ ν•˜λ“œμ½”λ”©λœ ν‚€/λΉ„λ°€λ²ˆν˜ΈλŠ” κΈˆμ§€ν•©λ‹ˆλ‹€.
  • 예: REACT_APP_API_URL, REACT_APP_GOOGLE_CLIENT_ID λ“±

πŸ“Œ [4] 반볡 둜직 관리

  • 반볡적으둜 μ‚¬μš©λ˜λŠ” ν•¨μˆ˜ λ˜λŠ” λ‘œμ§μ€ common/, utils/ λ“± 곡톡 μœ ν‹Έ 디렉토리에 λͺ¨λ“ˆν™”ν•©λ‹ˆλ‹€.
  • μ½”λ“œ 쀑볡을 쀄이고 μœ μ§€λ³΄μˆ˜λ₯Ό μš©μ΄ν•˜κ²Œ ν•©λ‹ˆλ‹€.


FrontEnd μ½”λ”© κ°€μ΄λ“œ

πŸ”— [1] μ»΄ν¬λ„ŒνŠΈ 섀계 원칙

βœ… μž¬μ‚¬μš©μ„±

  • 반볡 μ‚¬μš©λ˜λŠ” UI μš”μ†ŒλŠ” μ»΄ν¬λ„ŒνŠΈν™”ν•©λ‹ˆλ‹€.
  • ν•˜λ‚˜μ˜ λͺ©μ λ§Œ μˆ˜ν–‰ν•˜λ„λ‘ μ„€κ³„ν•˜λ©° λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ€ μ΅œλŒ€ν•œ λΆ„λ¦¬ν•©λ‹ˆλ‹€.

βœ… 뢄리

  • ν•˜λ‚˜μ˜ 파일이 λ„ˆλ¬΄ κΈΈκ±°λ‚˜ λ³΅μž‘ν•΄μ§€λŠ” 경우 κΈ°λŠ₯ λ‹¨μœ„λ‘œ μ»΄ν¬λ„ŒνŠΈλ₯Ό λΆ„λ¦¬ν•©λ‹ˆλ‹€.

βœ… 넀이밍

  • 역할이 λͺ…ν™•ν•˜κ²Œ λ“œλŸ¬λ‚˜λŠ” 넀이밍을 μ‚¬μš©ν•©λ‹ˆλ‹€.
  • ButtonComponent, InputComponent와 같은 **λΆˆν•„μš”ν•œ 접미사(Component)**λŠ” μ§€μ–‘ν•©λ‹ˆλ‹€.

πŸ—‚οΈ [ν˜„μž¬ μ»΄ν¬λ„ŒνŠΈ ꡬ쑰]

src/components/
β”œβ”€β”€ Common        # 곡톡 μš”μ†Œ (λ²„νŠΌ, 인풋, λ‘œλ”© λ“±)
β”œβ”€β”€ Layouts       # νŽ˜μ΄μ§€ λ ˆμ΄μ•„μ›ƒ κ΄€λ ¨
β”œβ”€β”€ Chart         # 루틴 톡계 μ‹œκ°ν™” μš”μ†Œ (μΊ˜λ¦°λ”, 데이, μΌ€λŸ¬μ…€ λ“±)
β”œβ”€β”€ Fortune       # μš΄μ„Έ κ΄€λ ¨ μ»΄ν¬λ„ŒνŠΈ
β”œβ”€β”€ Pokemon       # 포켓λͺ¬ κ΄€λ ¨ μ»΄ν¬λ„ŒνŠΈ
└── Routines      # 루틴 κ΄€λ ¨ μ»΄ν¬λ„ŒνŠΈ

πŸ”— [2] μ „μ—­ μƒνƒœ 관리

βœ… μ‚¬μš© 도ꡬ

βœ… μƒνƒœ 관리 ꡬ쑰

src/store/
β”œβ”€β”€ slices/
β”‚   └── user.js
β”œβ”€β”€ thunks/
β”‚   └── user.js
└── store.js

βœ… store.js μ˜ˆμ‹œ

// src/store/store.js
import { configureStore } from "@reduxjs/toolkit";
import userReducer from "./slices/user";
// import new slice!!

export const store = configureStore({
  reducer: {
    user: userReducer,
    //                  <- add here!!!
  },
});

βœ… Thunk μ˜ˆμ‹œ

// 1. thunk νŒŒμΌμ— thunkλ₯Ό μƒμ„±ν•œλ‹€.
// src/store/thunks/user.js
...
export const addMyRoutine = createAsyncThunk("addMyRoutine", async (routineId) => {
  const {
    data: { result },
  } = await axiosInstance.post(`${prefix}/routines/${routineId}`);
  return result;
});
...

// 2. ν•΄λ‹Ή λ„λ©”μΈμ˜ slice μ—μ„œ import ν•˜κ³ , 비동기 μš”μ²­ ν›„ μˆ˜ν–‰ν•  λ™μž‘ reducer에 μΆ”κ°€
// src/store/slices/user.js
const userSlice = createSlice({
  name: "user",
  ...
  extraReducers: (builder) => {
	  ...
	  builder.addCase(addMyRoutine.fulfilled, (state, action) => {
      const routineName = action.payload.routineName;
      const routineId = action.meta.arg;
      state.myRoutines = [...state.myRoutines, { routineId, name: routineName, isCompeleted: false }];
      console.log("addMyRoutine Result:", state.myRoutines);
    });
    ...
  },
});

// 3. μ‚¬μš© μ‹œ useDispatch 및 dispatch둜 μ‚¬μš©
// src/components/routines/RoutineCard.jsx
const dispatch = useDispatch();

...

const onClick = useCallback(() => {
    if (isRegistered) {
			...
    } else {
      dispatch(addMyRoutine(routineId)); //here!!
    }
  }, [dispatch, routineId, isRegistered]);

βœ… μƒνƒœ μ‚¬μš© μ˜ˆμ‹œ

// src/store/slices/user.js

export const selectMyInfo = (state) => state.user?.me; //here!!

// src/pages/MyPage.jsx
import { selectMyInfo } from "../store/slices/user"; //here!!

const MyPage = () => {
	...
	const user = useSelector(selectMyInfo); //here!!
	...
}

πŸ”— [3] API 톡신 ꡬ쑰

βœ… API 톡신 라이브러리

βœ… 디렉토리 ꡬ쑰

src/apis/
β”œβ”€β”€ UserApi.js
β”œβ”€β”€ RoutineApi.js
β”œβ”€β”€ FortuneApi.js
└── PokeApi.js

βœ… API 호좜 μ˜ˆμ‹œ

// μ‚¬μš© μ˜ˆμ‹œ
// src/apis/RoutineApi.js
...

/**
 * @param {number} year
 * @param {number} month
 * @returns {Promise<{routineId: number; routineName: string; daysStatistics: {}}[]>}
 */
export async function fetchMyStatistics(year, month) {
  try {
    const date = dayjs(new Date(year, month - 1)).format("YYYY-MM-DD");
    const {
      data: {
        result: { statistics },
      },
    } = await axiosInstance.get(`${prefix}/routines/${date}/statistics`);
    return statistics;
  } catch (error) {
    console.error(error);
    throw error;
  }
}

...

//src/pages/ChartPage.jsx
import { fetchMyStatistics } from "../apis/RoutineApi";

...

useEffect(() => {
    if (!isLoading) {
	    // here!!
      fetchMyStatistics(selectedDate.year, selectedDate.month).then((s) => {
        setStatistics(s);
      });
    }
  }, [isLoading, selectedDate]);
  
...

❗ 직접 axiosλ₯Ό import ν•˜μ§€ μ•Šκ³ , λ°˜λ“œμ‹œ api λͺ¨λ“ˆμ—μ„œ ν•¨μˆ˜λ‘œ μΆ”μΆœν•΄μ„œ μ‚¬μš©ν•˜μ„Έμš”.



μž‘μ—… κΈ°μ—¬ κ°€μ΄λ“œ


1. Issues 기반 μž‘μ—… ν”„λ‘œμ„ΈμŠ€

λͺ¨λ“  개발 μž‘μ—…μ€ λ°˜λ“œμ‹œ GitHub 이슈λ₯Ό μƒμ„±ν•œ 뒀에 μ‹œμž‘ν•©λ‹ˆλ‹€. μ΄μŠˆκ°€ μƒμ„±λ˜λ©΄ μžλ™μœΌλ‘œ κ³ μœ ν•œ λ²ˆν˜Έκ°€ λΆ€μ—¬λ˜λ©°, ν•΄λ‹Ή 번호λ₯Ό κΈ°μ€€μœΌλ‘œ 브랜치λͺ…, 컀밋 λ©”μ‹œμ§€, PR 제λͺ©μ„ μž‘μ„±ν•©λ‹ˆλ‹€.

μ˜ˆμ‹œ:

#35 루틴 μˆ˜ν–‰μ—¬λΆ€ κΈ°λŠ₯ 버그 μˆ˜μ •


2. Git 브랜치 κ·œμΉ™

브랜치 이름은 λ‹€μŒ ν˜•μ‹μ„ λ”°λ¦…λ‹ˆλ‹€:

{컀밋 λ©”μ‹œμ§€ μ»¨λ²€μ…˜ νƒ€μž…}: #{이슈번호} - {κ°„λ‹¨ν•œμ„€λͺ…}

μ˜ˆμ‹œ:

feat: #9 둜그인 UI 개발
refactor: #42 μš΄μ„ΈνŽ˜μ΄μ§€_λ¦¬νŒ©ν† λ§
hotfix: #50 배포 ν›„ https μš”μ²­ 였λ₯˜ μˆ˜μ •


3. Git 컀밋 λ©”μ‹œμ§€ μ»¨λ²€μ…˜

컀밋 λ©”μ‹œμ§€λŠ” λ‹€μŒ ν˜•μ‹μ„ μ§€μΌœμ£Όμ„Έμš”. Git branch κ·œμΉ™κ³Ό μœ μ‚¬ν•˜λ‚˜ λ‚΄μš©μ˜ 상세 μ •λ„μ˜ 차이가 μžˆμŠ΅λ‹ˆλ‹€.

{νƒ€μž…}: #{이슈번호} {λ‚΄μš©}

βœ… νƒ€μž… λͺ©λ‘

νƒ€μž… μ„€λͺ…
feat μƒˆλ‘œμš΄ κΈ°λŠ₯ μΆ”κ°€
fix 버그 μˆ˜μ •
refactor μ½”λ“œ λ¦¬νŒ©ν† λ§
style μ½”λ“œ ν¬λ§·νŒ…, μ„Έλ―Έμ½œλ‘  λˆ„λ½, μ½”λ“œ λ³€κ²½ μ—†μŒ
docs λ¬Έμ„œ μˆ˜μ •
chore 기타 변경사항 (λΉŒλ“œ 슀크립트 μˆ˜μ • λ“±)
test ν…ŒμŠ€νŠΈ κ΄€λ ¨ μ½”λ“œ μΆ”κ°€/μˆ˜μ •
perf μ„±λŠ₯ ν–₯상

βœ… 컀밋 λ©”μ‹œμ§€ μ˜ˆμ‹œ

feat: #35 루틴 λ―Έμˆ˜ν–‰ μ‹œ κ²½κ³  λ©”μ‹œμ§€ μΆ”κ°€
fix: #35 루틴 μˆ˜ν–‰ μ—¬λΆ€ 체크 버그 μˆ˜μ •
docs: #40 README μ—…λ°μ΄νŠΈ
refactor: #42 포켓λͺ¬ 뽑기기 κ²°κ³Ό μ»΄ν¬λ„ŒνŠΈ ꡬ쑰 κ°œμ„ 


4. Pull Request (PR) μž‘μ„± κ·œμΉ™

PR 제λͺ©λ„ 이슈 번호 기반으둜 μž‘μ„±ν•©λ‹ˆλ‹€. Git branch μ „λž΅μ„ 따라왔닀면 μžλ™μ μœΌλ‘œ 제λͺ©μ΄ μƒμ„±λ©λ‹ˆλ‹€.

PR λ³Έλ¬Έμ—μ„œλŠ” 보닀 μžμ„Έν•œ μ„€λͺ…을 μž‘μ„±ν•΄μ£Όμ„Έμš”. μ΄λ―Έμ§€λ‚˜ μ½”λ“œμ— λŒ€ν•œ μ„€λͺ…을 μ μ–΄μ£Όμ‹œλ©΄ μ›ν™œν•œ μ†Œν†΅μ΄ κ°€λŠ₯ν•©λ‹ˆλ‹€.

#{이슈번호} {λ³€κ²½ μš”μ•½}

μ˜ˆμ‹œ:

fix: #35 루틴 λ―Έμˆ˜ν–‰ κΈ°λŠ₯ κ΄€λ ¨ 버그 μˆ˜μ •
refactor: #42 포켓λͺ¬ 뽑기 κ²°κ³Ό νŽ˜μ΄μ§€ UI κ°œμ„ 

πŸ“‹ PR λ³Έλ¬Έ μ˜ˆμ‹œ

πŸ’‘ μž‘μ—… κ°œμš”
- 루틴 μˆ˜ν–‰ μ—¬λΆ€ 체크 λ‘œμ§μ—μ„œ null 체크가 λˆ„λ½λ˜μ–΄ λ°œμƒν•˜λ˜ 버그λ₯Ό μˆ˜μ •ν–ˆμŠ΅λ‹ˆλ‹€.

## βœ… λ³€κ²½ 사항

- 루틴 μ™„λ£Œ μ—¬λΆ€ νŒλ‹¨ 둜직 μˆ˜μ •
- κ²½κ³  λ©”μ‹œμ§€ μΆ”κ°€
- κ΄€λ ¨ μœ λ‹› ν…ŒμŠ€νŠΈ μΆ”κ°€

## πŸ”— κ΄€λ ¨ 이슈

- Closes #35


5. Pull Request (PR) MERGE μœ μ˜μ‚¬ν•­

FortuneMon의 PR은 μŠΉμΈμ„ 기반으둜 MERGEκ°€ κ°€λŠ₯ν•©λ‹ˆλ‹€.

총 4λͺ…μ˜ νŒ€μ› 쀑 4λͺ…이 λͺ¨λ‘ μŠΉμΈν•΄μ•Ό MERGEκ°€ ν™œμ„±ν™”λ©λ‹ˆλ‹€.

이λ₯Ό 톡해 μ½”λ“œμ˜ ν’ˆμ§ˆκ³Ό μ•ˆμ •μ„±μ„ ν™•λ³΄ν•˜κ³ , λͺ¨λ“  νŒ€μ›μ΄ 변경사항을 인지할 수 μžˆλ„λ‘ ν•©λ‹ˆλ‹€.


μž‘μ„±μž: FortuneMon ν˜‘μ—…μ˜ 기술

μ—…λ°μ΄νŠΈ: 2025-06-15