Skip to content
Β 
Β 

Latest commit

Β 

History

103 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

μ•„λž˜λŠ” μ •λ¦¬λœ GitHub README.md νŒŒμΌμž…λ‹ˆλ‹€. λΆˆν•„μš”ν•œ 쀑볡을 μ œκ±°ν•˜κ³  논리적 흐름에 따라 λ‚΄μš©μ„ μž¬κ΅¬μ„±ν–ˆμŠ΅λ‹ˆλ‹€.


COMAtching

COMAtching은 μ‚¬μš©μžμ˜ μ·¨λ―Έ, μ„±ν–₯, μ„ ν˜Έλ„λ₯Ό 기반으둜 졜적의 맀칭을 μ œκ³΅ν•˜λŠ” μ›Ή μ• ν”Œλ¦¬μΌ€μ΄μ…˜μž…λ‹ˆλ‹€.
이 ν”„λ‘œμ νŠΈμ—μ„œ ν”„λ‘ νŠΈμ—”λ“œλ₯Ό λ‹΄λ‹Ήν–ˆμœΌλ©°, μ£Όμš” 기술 μŠ€νƒμœΌλ‘œ React, Vanilla CSS, Recoil, Firebase 등을 μ‚¬μš©ν–ˆμŠ΅λ‹ˆλ‹€.


πŸš€ ν”„λ‘œμ νŠΈ μ‹œμ—°

COMAtching μ‹œμ—° μ˜μƒ


πŸ”§ μ£Όμš” κΈ°λŠ₯

  1. 카카였 둜그인

    • μ‚¬μš©μžκ°€ 카카였 λ‘œκ·ΈμΈμ„ 톡해 μ†μ‰½κ²Œ 접속할 수 μžˆμŠ΅λ‹ˆλ‹€.
  2. μ‚¬μš©μž μ·¨λ―Έ 선택

    • μ‚¬μš©μžκ°€ μ·¨λ―Έ 및 ν™œλ™μ„ μ„ νƒν•˜μ—¬ λ§žμΆ€ν˜• ν”„λ‘œν•„μ„ μƒμ„±ν•©λ‹ˆλ‹€.
  3. λ§€μΉ­ κ²°κ³Ό 확인

    • μ„ νƒλœ 쑰건(μ·¨λ―Έ, λ‚˜μ΄ λ“±)에 따라 졜적의 λ§€μΉ­ κ²°κ³Όλ₯Ό ν™•μΈν•©λ‹ˆλ‹€.
  4. λ°€μ–΄μ„œ μ»€ν”Œλ˜κΈ° κΈ°λŠ₯

    • μ‚¬μš©μžκ°€ λ²„νŠΌμ„ λ“œλž˜κ·Έν•˜μ—¬ μ§κ΄€μ μœΌλ‘œ 맀칭을 μ§„ν–‰ν•˜κ³  쑰건을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.
  5. λΈŒλΌμš°μ € λ¦¬λ””λ ‰μ…˜ 처리

    • μΉ΄μΉ΄μ˜€ν†‘κ³Ό 같은 인앱 λΈŒλΌμš°μ €μ—μ„œ 링크λ₯Ό μ—΄ 경우 크둬 λ“± μ™ΈλΆ€ λΈŒλΌμš°μ €λ‘œ λ¦¬λ””λ ‰μ…˜λ©λ‹ˆλ‹€.
  6. κ΄€λ¦¬μž νŽ˜μ΄μ§€

    • κ΄€λ¦¬μž μ „μš© νŽ˜μ΄μ§€μ—μ„œ μΆ©μ „ μš”μ²­μ„ μ‹€μ‹œκ°„μœΌλ‘œ 확인 및 μ²˜λ¦¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ–₯️ 배포

Firebase Hosting을 톡해 COMAtching을 λ°°ν¬ν–ˆμŠ΅λ‹ˆλ‹€.

πŸ”— COMAtching 배포 링크


βš™οΈ 기술 μŠ€νƒ

  • React: μ»΄ν¬λ„ŒνŠΈ 기반 μ‚¬μš©μž μΈν„°νŽ˜μ΄μŠ€
  • Vanilla CSS: μΌκ΄€λœ λ””μžμΈκ³Ό κΉ”λ”ν•œ μŠ€νƒ€μΌλ§
  • Recoil: κΈ€λ‘œλ²Œ μƒνƒœ 관리 라이브러리
  • Axios: μ„œλ²„μ™€μ˜ 효율적인 톡신 (API μš”μ²­ 및 응닡 처리)
  • SockJS & Stomp.js: μ‹€μ‹œκ°„ WebSocket 톡신을 μœ„ν•œ 라이브러리
  • Firebase Hosting: μ•ˆμ •μ μ΄κ³  λΉ λ₯Έ μ›Ή μ„œλΉ„μŠ€ 배포

πŸ“‚ 파일 ꡬ쑰

src/
β”œβ”€β”€ pages/                       # μ£Όμš” νŽ˜μ΄μ§€ μ»΄ν¬λ„ŒνŠΈ
β”‚   β”œβ”€β”€ Mainpage.jsx             # 메인 νŽ˜μ΄μ§€
β”‚   β”œβ”€β”€ Hobbyform.jsx            # μ·¨λ―Έ 선택 νŽ˜μ΄μ§€
β”‚   β”œβ”€β”€ Matchresult.jsx          # λ§€μΉ­ κ²°κ³Ό νŽ˜μ΄μ§€
β”‚   β”œβ”€β”€ Adminpage_unlogin.jsx    # κ΄€λ¦¬μž 둜그인 νŽ˜μ΄μ§€
β”‚   └── User_info_page.jsx       # μ‚¬μš©μž 정보 νŽ˜μ΄μ§€
β”‚
β”œβ”€β”€ components/                  # μž¬μ‚¬μš© κ°€λŠ₯ν•œ UI μ»΄ν¬λ„ŒνŠΈ
β”‚   └── AdminRequestList.jsx     # κ΄€λ¦¬μž μš”μ²­ 리슀트
β”‚
β”œβ”€β”€ recoil/                      # Recoil μƒνƒœ 관리
β”‚   β”œβ”€β”€ userState.js             # μ‚¬μš©μž μƒνƒœ 관리
β”‚   β”œβ”€β”€ adminRequests.js         # κ΄€λ¦¬μž μš”μ²­ μƒνƒœ
β”‚   └── MatchResultState.js      # λ§€μΉ­ κ²°κ³Ό μƒνƒœ
β”‚
β”œβ”€β”€ App.js                       # 메인 루트 μ»΄ν¬λ„ŒνŠΈ
β”œβ”€β”€ axiosConfig.js               # Axios μ„€μ • 파일
└── ...

πŸ“‘ μ‹€μ‹œκ°„ WebSocket 톡신 - κ΄€λ¦¬μž μΆ©μ „ μš”μ²­ 관리

SockJS와 Stomp.jsλ₯Ό μ‚¬μš©ν•΄ μ‹€μ‹œκ°„ WebSocket 톡신을 κ΅¬ν˜„ν–ˆμŠ΅λ‹ˆλ‹€. 이λ₯Ό 톡해 μΆ©μ „ μš”μ²­ 및 승인/μ·¨μ†Œ μ΄λ²€νŠΈκ°€ μ‹€μ‹œκ°„μœΌλ‘œ λ°˜μ˜λ©λ‹ˆλ‹€.

μ£Όμš” κΈ°λŠ₯

  1. μΆ©μ „ μš”μ²­ μ‹€μ‹œκ°„ ꡬ독

    • μ„œλ²„μ˜ /topic/chargeRequestsλ₯Ό ꡬ독해 μƒˆλ‘œμš΄ μš”μ²­μ„ μ‹€μ‹œκ°„μœΌλ‘œ μ—…λ°μ΄νŠΈν•©λ‹ˆλ‹€.
  2. 승인 및 μ·¨μ†Œ 처리

    • /topic/approvalUpdate와 /topic/cancelUpdateλ₯Ό κ΅¬λ…ν•˜μ—¬ μš”μ²­ μƒνƒœλ₯Ό μ¦‰μ‹œ λ³€κ²½ν•©λ‹ˆλ‹€.
  3. 토큰 기반 인증

    • μΏ ν‚€μ—μ„œ Authorization 토큰을 가져와 WebSocket μ—°κ²° μ‹œ μΈμ¦ν•©λ‹ˆλ‹€.

μ£Όμš” μ½”λ“œ

1. WebSocket μ—°κ²° 및 ꡬ독

useEffect(() => {
  const socket = new SockJS("https://cuk.comatching.site/wss");
  const client = Stomp.over(socket);
  const token = getTokenFromCookie();

  if (!token) {
    navigate("/adminlogin");
    return;
  }

  client.connect({ Authorization: `Bearer ${token}` }, (frame) => {
    console.log("Connected: " + frame);

    client.subscribe("/topic/chargeRequests", (message) => {
      const chargeRequests = JSON.parse(message.body);
      setRequests((prev) => [
        ...prev,
        ...chargeRequests.filter((req) => !prev.some((r) => r.userId === req.userId)),
      ]);
    });

    client.subscribe("/topic/approvalUpdate", (message) => {
      const userId = message.body;
      setRequests((prev) => prev.filter((req) => req.userId !== userId));
    });
  });
}, []);

2. μΏ ν‚€μ—μ„œ 토큰 κ°€μ Έμ˜€κΈ°

function getTokenFromCookie() {
  const name = "Authorization=";
  const decodedCookie = decodeURIComponent(document.cookie);
  const ca = decodedCookie.split(";");
  for (let i = 0; i < ca.length; i++) {
    let c = ca[i].trim();
    if (c.indexOf(name) === 0) {
      return c.substring(name.length, c.length);
    }
  }
  return "";
}

πŸ› οΈ Recoil μƒνƒœ 관리

Recoil을 μ‚¬μš©ν•΄ μ „μ—­ μƒνƒœλ₯Ό κ΄€λ¦¬ν•©λ‹ˆλ‹€.

μ‚¬μš©μž μƒνƒœ μ˜ˆμ‹œ

import { atom } from "recoil";

export const userState = atom({
  key: "userState",
  default: {
    username: "",
    age: "",
    mbti: "",
    hobby: [],
    point: 0,
  },
});

πŸ› οΈ Axios μ„€μ • 및 μ „μ—­ μ—λŸ¬ 처리

ν”„λ‘œμ νŠΈμ—μ„œ μ„œλ²„μ™€μ˜ 효율적이고 μ•ˆμ „ν•œ 톡신을 μœ„ν•΄ Axiosλ₯Ό μ„€μ •ν•˜μ˜€μœΌλ©°, μ „μ—­ μš”μ²­ 및 응닡 인터셉터λ₯Ό 톡해 μ—λŸ¬λ₯Ό μ€‘μ•™μ—μ„œ μ²˜λ¦¬ν•˜λ„λ‘ κ΅¬ν˜„ν–ˆμŠ΅λ‹ˆλ‹€.


μ£Όμš” κΈ°λŠ₯

  1. Axios μΈμŠ€ν„΄μŠ€ μ„€μ •

    • baseURL을 톡해 곡톡 μ„œλ²„ μ£Όμ†Œλ₯Ό μ„€μ •ν•©λ‹ˆλ‹€.
    • μΏ ν‚€μ—μ„œ μ—‘μ„ΈμŠ€ 토큰을 가져와 Authorization 헀더에 μžλ™μœΌλ‘œ μΆ”κ°€ν•©λ‹ˆλ‹€.
    • ν•„μš” μ‹œ μ„œλ²„λ‘œλΆ€ν„° μƒˆλ‘œμš΄ 토큰을 μˆ˜μ‹ ν•˜λ©΄ μΏ ν‚€λ₯Ό μ—…λ°μ΄νŠΈν•©λ‹ˆλ‹€.
  2. μ „μ—­ μ—λŸ¬ 처리

    • 401 인증 μ—λŸ¬: μ„Έμ…˜ 만료 μ‹œ μΏ ν‚€λ₯Ό μ‚­μ œν•˜κ³  메인 νŽ˜μ΄μ§€λ‘œ λ¦¬λ‹€μ΄λ ‰νŠΈν•©λ‹ˆλ‹€.
    • 403 κΆŒν•œ μ—λŸ¬: κΆŒν•œμ΄ μ—†λŠ” μž‘μ—…μ— λŒ€ν•΄ μ•Œλ¦Όμ„ ν‘œμ‹œν•©λ‹ˆλ‹€.
    • 404 μžμ› μ—†μŒ: μš”μ²­ν•œ μžμ›μ„ 찾을 수 없을 λ•Œ μ‚¬μš©μžμ—κ²Œ μ•Œλ¦Όμ„ μ œκ³΅ν•©λ‹ˆλ‹€.
    • 500 μ„œλ²„ μ—λŸ¬: μ„œλ²„ 였λ₯˜ λ°œμƒ μ‹œ μ•ˆλ‚΄ λ©”μ‹œμ§€λ₯Ό 좜λ ₯ν•©λ‹ˆλ‹€.
    • λ„€νŠΈμ›Œν¬ μ—λŸ¬: μ„œλ²„κ°€ λ‹€μš΄λ˜μ—ˆκ±°λ‚˜ λ„€νŠΈμ›Œν¬ 문제 λ°œμƒ μ‹œ μ‚¬μš©μžμ—κ²Œ μ•Œλ¦Όμ„ ν‘œμ‹œν•©λ‹ˆλ‹€.

μ„€μ • μ½”λ“œ

1. Axios μΈμŠ€ν„΄μŠ€ 생성 및 μš”μ²­ 인터셉터

  • μΏ ν‚€μ—μ„œ Authorization 토큰을 가져와 헀더에 μΆ”κ°€ν•©λ‹ˆλ‹€.
  • 토큰이 μ—†λŠ” 경우 둜그인 νŽ˜μ΄μ§€λ‘œ λ¦¬λ‹€μ΄λ ‰νŠΈλ©λ‹ˆλ‹€.
instance.interceptors.request.use(
  (config) => {
    const accessToken = Cookies.get("Authorization");
    if (!accessToken) {
      window.location.href = "/";
      return Promise.reject(new Error("No access token found"));
    }
    config.headers.Authorization = `Bearer ${accessToken}`;
    return config;
  },
  (error) => Promise.reject(error)
);

2. 응닡 인터셉터 및 토큰 μ—…λ°μ΄νŠΈ

  • μ„œλ²„μ—μ„œ μƒˆλ‘œμš΄ 토큰을 μ œκ³΅ν•˜λ©΄ μΏ ν‚€λ₯Ό μ—…λ°μ΄νŠΈν•©λ‹ˆλ‹€.
instance.interceptors.response.use(
  (response) => {
    const newAccessToken = response.headers["authorization"];
    if (newAccessToken && newAccessToken.startsWith("Bearer ")) {
      const tokenWithoutBearer = newAccessToken.slice(7);
      Cookies.remove("Authorization", { path: "/" });
      Cookies.set("Authorization", tokenWithoutBearer, { path: "/" });
    }
    return response;
  },
  async (error) => {
    if (!error.response) {
      alert("μ„œλ²„μ— μ—°κ²°ν•  수 μ—†μŠ΅λ‹ˆλ‹€. λ‹€μ‹œ μ‹œλ„ν•΄ μ£Όμ„Έμš”.");
      return Promise.reject(error);
    }
    const { status, data } = error.response;

    // μ „μ—­ μ—λŸ¬ 처리
    if (status === 401 && (data.code === "SEC-001" || data.code === "SEC-002")) {
      Cookies.remove("Authorization");
      alert("μ„Έμ…˜μ΄ λ§Œλ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€. λ‹€μ‹œ λ‘œκ·ΈμΈν•΄ μ£Όμ„Έμš”.");
      window.location.href = "/";
    } else if (status === 403) {
      alert("ν•΄λ‹Ή μž‘μ—…μ— λŒ€ν•œ κΆŒν•œμ΄ μ—†μŠ΅λ‹ˆλ‹€.");
    } else if (status === 404) {
      alert("μš”μ²­ν•œ μžμ›μ΄ μ‘΄μž¬ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.");
    } else if (status === 500) {
      alert("μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€. μž μ‹œ ν›„ λ‹€μ‹œ μ‹œλ„ν•΄ μ£Όμ„Έμš”.");
    }
    return Promise.reject(error);
  }
);

μ‹€ν–‰ 흐름

  1. μš”μ²­:

    • Axios μΈμŠ€ν„΄μŠ€κ°€ Authorization μΏ ν‚€λ₯Ό ν™•μΈν•˜κ³  헀더에 μΆ”κ°€ν•©λ‹ˆλ‹€.
    • 토큰이 μ—†μœΌλ©΄ 메인 νŽ˜μ΄μ§€(/)둜 λ¦¬λ‹€μ΄λ ‰νŠΈν•©λ‹ˆλ‹€.
  2. 응닡:

    • μ„œλ²„ μ‘λ‹΅μ—μ„œ μƒˆλ‘œμš΄ 토큰이 μ „λ‹¬λ˜λ©΄ κΈ°μ‘΄ μΏ ν‚€λ₯Ό μ‚­μ œν•˜κ³  μƒˆ ν† ν°μœΌλ‘œ μ—…λ°μ΄νŠΈν•©λ‹ˆλ‹€.
    • μ—λŸ¬κ°€ λ°œμƒν•˜λ©΄ μƒνƒœ μ½”λ“œμ— 따라 μ‚¬μš©μžμ—κ²Œ μ μ ˆν•œ μ•ˆλ‚΄ λ©”μ‹œμ§€λ₯Ό 좜λ ₯ν•©λ‹ˆλ‹€.

μ „μ—­ μ—λŸ¬ 처리 κ²°κ³Ό

  • μ„Έμ…˜ 만료: μΏ ν‚€ μ‚­μ œ 및 둜그인 νŽ˜μ΄μ§€λ‘œ 이동
  • κΆŒν•œ 였λ₯˜: μ‚¬μš©μž μ•Œλ¦Ό (403)
  • μžμ› μ—†μŒ: μ•Œλ¦Ό λ©”μ‹œμ§€ 좜λ ₯ (404)
  • μ„œλ²„ 였λ₯˜: μ‚¬μš©μžμ—κ²Œ 였λ₯˜ μ•ˆλ‚΄ (500)
  • λ„€νŠΈμ›Œν¬ μ—λŸ¬: μ„œλ²„ μ—°κ²° λΆˆκ°€ λ©”μ‹œμ§€ 좜λ ₯

이 섀정을 톡해 Axios μš”μ²­κ³Ό 응닡을 효율적으둜 κ΄€λ¦¬ν•˜κ³  μ—λŸ¬λ₯Ό μ „μ—­μ—μ„œ μ²˜λ¦¬ν•˜μ—¬ μ‚¬μš©μž κ²½ν—˜μ„ κ°œμ„ ν–ˆμŠ΅λ‹ˆλ‹€. πŸš€


πŸ“š μ‚¬μš©λœ 라이브러리

  • SockJS: WebSocket μ—°κ²° 및 λΈŒλΌμš°μ € ν˜Έν™˜μ„±
  • Stomp.js: WebSocket λ©”μ‹œμ§€ 전솑 및 ꡬ독
  • Clipboard API: ν΄λ¦½λ³΄λ“œμ— ν…μŠ€νŠΈ 볡사 κΈ°λŠ₯
  • Axios: API 톡신
  • Recoil: μƒνƒœ 관리 라이브러리

πŸ–ΌοΈ ν”„λ‘œμ νŠΈ 이미지

COMAtching κ°€μ΄λ“œ νŽ˜μ΄μ§€
COMAtching 둜고


이 λ¬Έμ„œλŠ” ν”„λ‘œμ νŠΈμ˜ μ£Όμš” κΈ°λŠ₯κ³Ό 기술적 κ΅¬ν˜„μ„ λͺ…ν™•ν•˜κ³  κ°„κ²°ν•˜κ²Œ μ„€λͺ…ν•©λ‹ˆλ‹€.

μ΅œμ’… 배포 링크: COMAtching πŸš€

About

Comatching

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages