Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
bbfb1f6
게시판 UI 컴포넌트 구현했습니다.
cmmoon03 Sep 24, 2025
3d72ce3
Update frontend/src/pages/Board.module.css
cmmoon03 Sep 25, 2025
ce7f10b
Update frontend/src/pages/Board.module.css
cmmoon03 Sep 25, 2025
0c3a3b3
Update frontend/src/pages/Board.module.css
cmmoon03 Sep 25, 2025
fa72546
Update frontend/src/pages/Board.module.css
cmmoon03 Sep 25, 2025
ba3d736
Update frontend/src/pages/Board.module.css
cmmoon03 Sep 25, 2025
8d714df
Merge remote-tracking branch 'origin' into SISC1-149-fe
cmmoon03 Sep 26, 2025
237edca
[FE] SISC1-11 [STYLE] : 검색 버튼 아이콘을 인라인 SVG에서 파일로 분리
cmmoon03 Sep 28, 2025
e7be1cf
[FE] SISC1-149 [refactor]: util 함수 위치 변경
gxuoo Oct 29, 2025
da16802
[FE] SISC1-149 [FIX]: 불필요한 코드 삭제
gxuoo Oct 29, 2025
297d848
[FE] SISC1-149 [FEAT] : 게시판 상세페이지 구현 및 반응형 디자인 적용
cmmoon03 Nov 1, 2025
d3964d8
[FE] SISC1-11 [FIX] : 잘못 올라간 코드들 재 전송 및 상세페이지 수정, 삭제 버튼 이미지 추가, 세션선택 …
cmmoon03 Nov 11, 2025
c5be2d0
[FE] SISC1-11 [FEAT] : 댓글 수정/삭제 기능 추가
cmmoon03 Nov 11, 2025
7e022ce
[FE] SISC1-149 [FIX] : posts 초기값 설정
DongEun02 Nov 12, 2025
6f99148
[FE] SISC1-11 [FIX] : 코드래빗이 언급한 부분 수정
cmmoon03 Nov 13, 2025
24eb51a
Revert "[FE] SISC1-149 [FIX] : 코드래빗이 언급한 부분 수정"
cmmoon03 Nov 13, 2025
f192945
[FE] SISC1-149 [FIX] : 코드래빗이 언급한 부분 중 사용자 부분 제외하고 수정
cmmoon03 Nov 13, 2025
e8196db
[FE] SISC1-149 [FIX] : 코드래빗이 언급한 부분 중 사용자 부분 제외하고 수정
cmmoon03 Nov 13, 2025
1af8a6e
[FE] SISC1-149 [FIX] : 윈도우 설정 파일 제거 및 .gitignore 추가
DongEun02 Nov 13, 2025
ef59b84
Merge branch 'main' into SISC1-149-fe
DongEun02 Nov 13, 2025
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ __pycache__/

# ===== Backend =====
backend/src/main/java/org/sejongisc/backend/stock/TestController.java

# ===== windows =====
.desktop.ini
2 changes: 2 additions & 0 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Layout from './components/Layout';
import Home from './pages/Home';
import Attendance from './pages/Attendance';
import Board from './pages/Board';
import PostDetail from './pages/PostDetail';
import StockGame from './pages/StockGame';
import BackTest from './pages/BackTest';
import Mypage from './pages/Mypage';
Expand All @@ -23,6 +24,7 @@ function App() {
<Route path="/attendance" element={<Attendance />} />
<Route path="/attendance-manage" element={<AttendanceManage />} />
<Route path="/board" element={<Board />} />
<Route path="/board/:postId" element={<PostDetail />} />
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

라우트 파라미터와 컴포넌트 구현 간 불일치를 확인하세요.

:postId 파라미터가 정의되어 있지만, PostDetail 컴포넌트는 이를 사용하지 않고 location.state에만 의존합니다. 이는 URL 공유 및 새로고침 시 문제를 발생시킬 수 있습니다.

일관성을 위해 URL 파라미터를 활용하거나, 현재 구현의 제약사항을 명확히 문서화하는 것을 권장합니다.

PostDetail.jsx의 리뷰 코멘트도 참고하세요.

🤖 Prompt for AI Agents
In frontend/src/App.jsx around line 32, the route defines a URL param (:postId)
but the PostDetail component currently ignores it and relies only on
location.state; update PostDetail to read postId from useParams() and load the
post when location.state is absent (e.g., fetch/post lookup by id), or if you
prefer to keep the current state-only approach, remove the :postId param from
the route and document that the component requires navigation state—ensure one
consistent behavior (preferably support both: useParams as primary source and
fallback to location.state).

<Route path="/quant-bot" element={<QuantBot />} />
<Route path="/stock-game" element={<StockGame />} />
<Route path="/back-test" element={<BackTest />} />
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/assets/boardBookMark.fill.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/src/assets/boardBookMark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/src/assets/boardCloseIcon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/src/assets/boardFolder.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/src/assets/boardHeart.fill.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/src/assets/boardHeart.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/src/assets/boardPencil.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/src/assets/boardSearchArrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/src/assets/boardSelectArrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/src/assets/boardSortArrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/src/assets/board_plus.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions frontend/src/assets/board_profile.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions frontend/src/components/Board/BoardActions.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import styles from './BoardActions.module.css';
import PlusIcon from '../../assets/board_plus.svg';
import DropdownArrowIcon from '../../assets/boardSelectArrow.svg';

const BoardActions = ({ sortOption, onSortChange, onWrite }) => {
return (
<div className={styles.boardActions}>
<div className={styles.selectWrapper}>
<select
className={styles.sortSelect}
value={sortOption}
onChange={(e) => onSortChange(e.target.value)}
>
<option value="latest">최신순</option>
<option value="oldest">오래된순</option>
<option value="popular">인기순</option>
</select>
<img
src={DropdownArrowIcon}
alt="드롭다운"
className={styles.selectArrow}
/>
</div>
Comment on lines +10 to +24
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

접근성: select 요소에 레이블이 필요합니다.

select 요소가 시각적으로 표시되지 않는 레이블 없이 사용되고 있습니다. 스크린 리더 사용자를 위해 aria-label 또는 연결된 <label> 요소를 추가해주세요.

       <div className={styles.selectWrapper}>
         <select
           className={styles.sortSelect}
           value={sortOption}
           onChange={(e) => onSortChange(e.target.value)}
+          aria-label="정렬 옵션"
         >
           <option value="latest">최신순</option>
           <option value="oldest">오래된순</option>
           <option value="popular">인기순</option>
         </select>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<select
className={styles.sortSelect}
value={sortOption}
onChange={(e) => onSortChange(e.target.value)}
>
<option value="latest">최신순</option>
<option value="oldest">오래된순</option>
<option value="popular">인기순</option>
</select>
<img
src={DropdownArrowIcon}
alt="드롭다운"
className={styles.selectArrow}
/>
</div>
<select
className={styles.sortSelect}
value={sortOption}
onChange={(e) => onSortChange(e.target.value)}
aria-label="정렬 옵션"
>
<option value="latest">최신순</option>
<option value="oldest">오래된순</option>
<option value="popular">인기순</option>
</select>
<img
src={DropdownArrowIcon}
alt="드롭다운"
className={styles.selectArrow}
/>
</div>
🤖 Prompt for AI Agents
In frontend/src/components/Board/BoardActions.jsx around lines 10 to 24, the
<select> is missing an accessible label; add one by either giving the select a
unique id and rendering a visible or visually-hidden <label
htmlFor="...">Sort</label>, or by adding an appropriate aria-label (e.g.,
aria-label="정렬 기준" or "Sort options") directly on the select; ensure the
onChange and value remain unchanged and update styles if you add a visible label
or apply a sr-only class for a hidden label.


<button className={styles.writeButton} onClick={onWrite}>
<span>글 작성하기</span>
<img src={PlusIcon} alt="작성" />
</button>
</div>
);
};

export default BoardActions;
118 changes: 118 additions & 0 deletions frontend/src/components/Board/BoardActions.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
:root {
--color-border: 0.6px solid rgba(0, 0, 0, 1);
--color-text: rgba(0, 0, 0, 1);
--color-bg-light: #fafafa;
--color-primary: #1d80f4;
--color-button-gradient: linear-gradient(
92.89deg,
#d8e8ff 6.95%,
#d1d8ff 74.81%,
#dddeff 107.39%
);
--font-family:
'Pretendard', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--font-regular: 400;
--font-size: 16px;
--line-height: 100%;
--letter-spacing: 0%;
--gap: 12px;
--radius: 4px;
--transition: all 0.2s;
}

.boardActions {
width: 100%;
max-width: calc(100% - 20px);
padding-right: 20px;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: flex-end;
gap: var(--gap);
margin-bottom: 32px;
}

.selectWrapper {
position: relative;
flex-shrink: 0;
}

.sortSelect {
width: 90px;
height: 35px;
padding: 8px;
padding-right: 20px;
border: var(--color-border);
border-radius: var(--radius);
background: #fff;
cursor: pointer;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
transition: var(--transition);
box-sizing: border-box;
font-family: var(--font-family);
font-weight: var(--font-regular);
font-size: var(--font-size);
line-height: var(--line-height);
letter-spacing: var(--letter-spacing);
color: var(--color-text);
}

.sortSelect:hover {
background: var(--color-bg-light);
}

.sortSelect:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(29, 128, 244, 0.1);
}

.selectArrow {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
pointer-events: none;
width: 14px;
height: 14px;
}

.writeButton {
display: flex;
align-items: center;
justify-content: center;
gap: var(--gap);
width: 122px;
height: 35px;
padding: 8px 12px;
background: var(--color-button-gradient);
border: none;
border-radius: var(--radius);
cursor: pointer;
transition: var(--transition);
white-space: nowrap;
font-family: var(--font-family);
font-weight: var(--font-regular);
font-size: var(--font-size);
line-height: var(--line-height);
letter-spacing: var(--letter-spacing);
color: var(--color-text);
box-sizing: border-box;
flex-shrink: 0;
}

.writeButton:hover {
opacity: 0.9;
box-shadow: 0 2px 8px rgba(29, 128, 244, 0.15);
}

.writeButton:active {
opacity: 0.8;
}

.writeButton img {
width: 9.67px;
height: 9.67px;
}
76 changes: 76 additions & 0 deletions frontend/src/components/Board/Modal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import styles from './Modal.module.css';
import FolderIcon from '../../assets/boardFolder.svg';
import CloseIcon from '../../assets/boardCloseIcon.svg';
import DropdownArrowIcon from '../../assets/boardSelectArrow.svg';

const Modal = ({ title, setTitle, content, setContent, onSave, onClose }) => {
return (
<div className={styles.overlay} onClick={onClose}>
<div className={styles.modal} onClick={(e) => e.stopPropagation()}>
<div className={styles.header}>
<h2 className={styles.title}>게시글 작성</h2>
<button className={styles.closeButton} onClick={onClose}>
<img src={CloseIcon} alt="닫기" />
</button>
</div>

<div className={styles.form}>
<div className={styles.titleField}>
<label className={styles.label}>제목</label>
<input
className={styles.input}
type="text"
placeholder="제목을 입력해주세요."
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</div>

<div className={styles.contentField}>
<label className={styles.label}>내용</label>
<div className={styles.contentContainer}>
<div
className={styles.fileSection}
onClick={() => document.getElementById('fileUpload').click()}
>
<img src={FolderIcon} alt="폴더" />
<span className={styles.fileText}>파일추가</span>
<input
type="file"
id="fileUpload"
className={styles.fileInput}
style={{ display: 'none' }}
/>
</div>
<div className={styles.divider}></div>
<textarea
className={styles.textarea}
placeholder="내용을 입력해주세요."
value={content}
onChange={(e) => setContent(e.target.value)}
/>
</div>
</div>

<div className={styles.accessField}>
<label className={styles.accessLabel}>접근 권한</label>
<div className={styles.selectWrapper}>
<select className={styles.select} defaultValue="세션선택">
<option value="세션선택">세션선택</option>
</select>
<div className={styles.selectIcon}>
<img src={DropdownArrowIcon} alt="드롭다운 화살표" />
</div>
</div>
</div>
Comment on lines +55 to +65
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

접근 권한 선택값이 저장되지 않음

Line 58에서 select 요소가 defaultValue를 사용하고 있어, 사용자가 선택한 값이 부모 컴포넌트로 전달되지 않습니다. 게시글 저장 시 접근 권한 정보가 누락됩니다.

부모 컴포넌트에 accessControl state와 setter를 추가하고, 제어 컴포넌트로 변경해야 합니다:

Board.jsx:

const [accessControl, setAccessControl] = useState('세션선택');

Modal.jsx:

-const Modal = ({ title, setTitle, content, setContent, onSave, onClose }) => {
+const Modal = ({ title, setTitle, content, setContent, accessControl, setAccessControl, onSave, onClose }) => {
   // ...
-  <select className={styles.select} defaultValue="세션선택">
+  <select 
+    className={styles.select} 
+    value={accessControl}
+    onChange={(e) => setAccessControl(e.target.value)}
+  >
🤖 Prompt for AI Agents
frontend/src/components/Board/Modal.jsx around lines 55 to 67: the select uses
defaultValue so user selection isn't propagated; convert the select into a
controlled input by accepting an accessControl prop and an
onChange/setAccessControl prop from the parent, replace defaultValue with
value={accessControl} and call the setter (e.g., onChange ->
setAccessControl(event.target.value)) to update parent state; also add
accessControl state and its setter in the parent Board.jsx (initialize to
'세션선택') and pass them into Modal, and ensure the parent includes accessControl
when saving the post.

</div>

<button className={styles.saveButton} onClick={onSave}>
저장
</button>
</div>
</div>
);
};

export default Modal;
Loading