Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions src/pages/GroupPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,32 @@ export default function GroupPage() {
description: string;
}) => {
try {
// 디버깅: 요청 전 데이터 확인
console.log("📤 보내는 데이터:", {
name: data.name,
meetingName: data.category ?? "", // undefined 방지
maxMembers: Number(data.totalMembers), // 숫자로 강제 변환
password: "1234",
description: data.description,
});
Comment on lines +39 to +46

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

디버깅을 위한 console.log 구문은 프로덕션 코드에 포함되어서는 안 됩니다. 머지하기 전에 제거해주세요.


const response = await createStudy({
name: data.name,
meetingName: data.category ?? "", // undefined 방지
maxMembers: Number(data.totalMembers), // number 강제 변환
password: "1234",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

비밀번호가 "1234"로 하드코딩되어 있습니다. 이는 심각한 보안 취약점입니다. 실제 사용자가 입력한 비밀번호를 사용하도록 수정해야 합니다. AddGroupModal에 비밀번호 입력 필드를 추가하고, 해당 값을 안전하게 전달하는 로직이 필요해 보입니다.

description: data.description,
});
Comment on lines +39 to 54
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove debug logging and hard‑coded password from group creation payload

This block both logs the outgoing payload and hard‑codes the group password:

console.log("📤 보내는 데이터:", { ... password: "1234", ... });

const response = await createStudy({
  name: data.name,
  meetingName: data.category ?? "",
  maxMembers: Number(data.totalMembers),
  password: "1234",
  description: data.description,
});

Two issues:

  • The log includes the password value, which is sensitive.
  • Using the same literal "1234" for every group makes the protection effectively useless beyond local prototyping.

Before merging to main, I’d (a) remove or dev‑gate this log and (b) either take password from user input or agree with BE on a different mechanism if a password isn’t really needed yet.

🤖 Prompt for AI Agents
In src/pages/GroupPage.tsx around lines 39 to 54, remove the console.log that
prints the outgoing payload (it exposes sensitive data) and eliminate the
hard‑coded "1234" password in the createStudy call; instead take the password
from the form data (e.g., data.password) or omit the password field entirely if
the backend agreed it’s not required, and ensure any remaining debug logs never
include password or other sensitive fields (use guarded logging or omit
sensitive keys).


// API 응답을 StudyGroup 형식으로 변환
const newGroup: StudyGroup = {
id: response.id, // UUID 그대로 사용
category: data.category, // 모달에서 입력받은 모임명
title: response.name,
id: response.id, // UUID
category: data.category, // 사용자가 입력한 모임명 유지
title: response.name, // 백엔드 name 사용
leader: "나", // 생성자는 자동으로 스터디장

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

스터디장 이름이 "나"로 하드코딩되어 있습니다. 현재 로그인한 사용자의 이름이나 닉네임으로 동적으로 설정하는 것이 좋습니다.

totalMembers: data.totalMembers, // 모달에서 입력받은 인원수
totalMembers: response.maxMembers, // ⚠️ 서버 응답값 사용
currentMembers: 1, // 생성자 포함
createdAt: response.createdAt, // 생성일시 저장 (정렬용)
createdAt: response.createdAt, // 백엔드 createdAt
};

setStudyGroups((prev) => [...prev, newGroup]);
Expand All @@ -60,6 +72,7 @@ export default function GroupPage() {
alert("스터디 생성 중 오류가 발생했습니다.");
}
};


// 정렬 적용된 배열 (createdAt 우선, 없으면 id 기준)
const sortedGroups = [...studyGroups].sort((a, b) => {
Expand Down
19 changes: 7 additions & 12 deletions src/services/api.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,34 @@
import type { AxiosInstance } from "axios";
import axios from "axios";
import type { AxiosInstance } from "axios";

// 🔥 axios 인스턴스 단 하나만 생성
const api: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
withCredentials: true, // 필요 없으면 제거 가능
// 기본 헤더는 설정하지 않음 (각 요청에서 필요에 따라 설정)
withCredentials: true,
});

// 요청 인터셉터
api.interceptors.request.use(
(config) => {
// Token 자동 삽입 (필요하면)
// 🔥 Access Token 자동 삽입
const token = localStorage.getItem("accessToken");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
// FormData를 보낼 때는 Content-Type을 제거하여 브라우저가 자동으로 boundary를 포함한 Content-Type을 설정하도록 함

// 🔥 FormData 처리
if (config.data instanceof FormData) {
// axios의 기본 Content-Type 헤더를 완전히 제거
// 브라우저가 자동으로 multipart/form-data; boundary=... 를 설정하도록 함
if (config.headers) {
// 모든 가능한 Content-Type 헤더 제거
delete config.headers["Content-Type"];
delete config.headers["content-type"];
// 명시적으로 undefined로 설정하여 axios가 헤더를 설정하지 않도록 함
config.headers["Content-Type"] = undefined as any;
}
} else {
// FormData가 아닐 때만 application/json 설정
if (!config.headers["Content-Type"]) {
config.headers["Content-Type"] = "application/json";
}
}

return config;
},
(error) => Promise.reject(error)
Expand Down
4 changes: 3 additions & 1 deletion src/services/study.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ import type {

/**
* 스터디 생성 API
* @param data 스터디 생성 요청 데이터 (name, description)
* @param data 스터디 생성 요청 데이터 (name, meetingName, maxMembers, password, description)
* @returns 생성된 스터디 정보
*/
export async function createStudy(
data: CreateStudyRequest
): Promise<CreateStudyResponse> {
console.log("📤 실제 요청 바디:", data);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

디버깅을 위한 console.log 구문은 프로덕션 코드에 포함되어서는 안 됩니다. 머지하기 전에 제거해주세요.

const response = await api.post<CreateStudyResponse>("/studies", data);
return response.data;
}
Comment on lines +11 to 20
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid logging full createStudy payload (includes password)

console.log("📤 실제 요청 바디:", data); will log all fields including password. Even if currently a dummy value, this is a bad pattern to keep in production and can leak credentials into logs. I’d remove this log or gate it behind a dev‑only check and, if needed, log only non‑sensitive fields.

🤖 Prompt for AI Agents
In src/services/study.service.ts around lines 11 to 20, the function logs the
entire createStudy request body (including the password) which can leak
credentials; remove the console.log or guard it behind an environment/dev-only
check and if logging is needed, only log a sanitized object that excludes
sensitive fields (e.g., omit password) before sending the request. Ensure no raw
request payload with sensitive fields is written to logs in production.



/**
* 초대 코드로 스터디 정보 조회 API
* 인증 불필요한 공개 API이므로 별도 인스턴스 사용
Expand Down
11 changes: 8 additions & 3 deletions src/types/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,22 @@ export interface StudyGroup {

export type SortOrder = "최신순" | "오래된순";

// API 응답 타입
// API 요청 타입
export interface CreateStudyRequest {
name: string;
description: string;
meetingName: string;
maxMembers: number;
password: string;
description?: string;
}

// API 응답 타입
export interface CreateStudyResponse {
id: string;
name: string;
inviteCode: string;
description: string;
meetingName: string;
maxMembers: number;
status: string;
createdAt: string;
}
Expand Down
8 changes: 8 additions & 0 deletions vercel.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{ "rewrites":
[
{
"source": "/api/:path*",
"destination": "http://3.27.86.20:8080/api/:path*"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

Vercel rewrite 설정에 보안 및 유지보수 관련 문제가 있습니다.

  1. HTTP 사용 (Critical): destinationhttp로 설정되어 있어, API 요청이 암호화되지 않은 상태로 전송됩니다. 이는 중간자 공격(MITM)에 취약하며, 민감한 정보가 노출될 수 있습니다. 반드시 https를 사용해야 합니다.
  2. 하드코딩된 IP 주소 (High): 백엔드 서버의 IP 주소가 하드코딩되어 있습니다. 서버 IP가 변경될 경우 배포된 프론트엔드가 동작하지 않게 됩니다. 가능하면 도메인 이름을 사용하고, 환경별로 설정할 수 있도록 관리하는 것이 좋습니다.
Suggested change
"destination": "http://3.27.86.20:8080/api/:path*"
"destination": "https://3.27.86.20:8080/api/:path*"

}
]
}