Skip to content

Conversation

@ghost
Copy link

@ghost ghost commented Jun 20, 2025

📌 변경 사항 개요

공고리스트 구현

📝 상세 내용

  • Gpt한테 물어보니까 공고 카드를 클릭하면 해당 공고 상세 페이지로 이동한다고 하는데 merge해보고 더 테스트가 필요할 듯 합니다
  • 맞춤 공고는 처음 그냥 최신 3개가 보여지고 필터로 적용했을때 주소 기준으로 나오게끔 설정은 했는데 이것도 테스트가 필요합니다
  • 전체 공고는 페이지네이션 기준으로 구현했습니다
  • 필터에서 조건을 선택하고 적용하기 혹은 초기화하기 했을때 공고에 반영이 되도록 구현했습니다
  • 그리고 검색기능 구현을 위해 nav.tsx의 state 추가 및 props추가 했습니다
  • 또 하나 noticeApi에 함수 추가해서 구현을 했는데 제가 사실 api문서를 보는게 아직 미숙하다보니 다른 방법이 있으시면 수정하겠습니다
  • 아직 반응형 구현이 제가 미숙하다보니 이 부분은 광민이 도와주기로 해서 기능 위주로 보시면 될거같고 지금 올리는 Pr은 광민님이 반응형 작업을 하기 위해 올리는 pr입니다!

🔗 관련 이슈

🖼️ 스크린샷(선택사항)

screencapture-localhost-5173-2025-06-20-19_03_59
screencapture-localhost-5173-search-2025-06-20-19_04_11 (1)
screencapture-localhost-5173-search-2025-06-20-19_04_28
screencapture-localhost-5173-2025-06-20-19_04_58 -> 정연은 중구로 주소로 설정되어있는데 제가 임의로 영등포로 하니까 정연 공고가 안 뜨는 사진

nav.tsx에 state,props추가(검색 기능 구현)

const [searchValue, setSearchValue] = useState('');
<input type="text" placeholder="가게 이름으로 찾아보세요" value={searchValue} onChange={(e) => setSearchValue(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter' && searchValue.trim()) { navigate(/search?query=${encodeURIComponent(searchValue.trim())}, ); } }} className="h-36 w-full rounded-[10px] bg-gray-10 pt-10 pb-10 pl-40 placeholder:text-body2 placeholder:text-gray-40 md:h-40 md:w-344 lg:w-450" />

noticeApi.ts에 함수 추가
export const getNotices = async (params: { offset?: number; limit?: number; address?: string; keyword?: string; startsAtGte?: string; hourlyPayGte?: number; sort?: 'time' | 'pay' | 'hour' | 'shop'; }) => { const query = new URLSearchParams( Object.entries(params) .filter(([, v]) => v !== undefined && v !== '') .map(([k, v]) => [k, String(v)]), ); const response = await api.get<GetNoticesResponse>(/notices?${query}); return response.data; };

💡 참고 사항

@ghost ghost linked an issue Jun 20, 2025 that may be closed by this pull request
Copy link
Contributor

@Moon-ju-young Moon-ju-young left a comment

Choose a reason for hiding this comment

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

PR 올라온 것들 확인하고 제때 최신 반영해주시면 서로 편할 것 같습니다~ 그리고 마크다운에서 코드 블럭은 백틱(`) 한 개가 아니라 3개로(```) 씁니다!

<Routes>
{/* 공통 페이지 */}
<Route path="/" element={<NoticeList />} />
<Route path="/search" element={<SearchPage />} />
Copy link
Contributor

@Moon-ju-young Moon-ju-young Jun 20, 2025

Choose a reason for hiding this comment

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

❓ 이미 페이지 구성은 정리된지라 구성을 변경하실 거라면 논의 후에 진행하셨으면 좋았을 것 같습니다~ 그리고 SearchPage 없이 구현이 가능할 것 같은데 어렵나요?

Copy link
Author

Choose a reason for hiding this comment

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

네 알겠습니다 merge이후에 리펙토링때 수정해놓겠습니다

search?: string;
};

// ===================== 상수 =====================
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// ===================== 상수 =====================
// 상수

💬 그냥 간단하게 주석을 적어주셔도 될 것 같습니다~

Copy link
Collaborator

Choose a reason for hiding this comment

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

넵 이건 수정했습니다

Comment on lines 17 to 19
type NoticeListProps = {
search?: string;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

💬 prop이 간단할 경우 굳이 type을 분리하지 않아도 될 것 같습니다~

Copy link
Author

Choose a reason for hiding this comment

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

네 알겠습니다

Copy link
Collaborator

Choose a reason for hiding this comment

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

요 부분도 바꿧습니다

Comment on lines 53 to 55
const [currentPage, setCurrentPage] = useState<number>(1); // 현재 페이지(페이지네이션)
const [filterOpen, setFilterOpen] = useState<boolean>(false); // 필터 모달 오픈
const [loading, setLoading] = useState(false); // 로딩 에러처리
Copy link
Contributor

Choose a reason for hiding this comment

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

💬 어디 부분은 generic을 사용하고 어느 부분은 사용하고 있지 않네요~ 기본형 들은 generic을 빼고 자동으로 추론되게 해도 괜찮을 것 같습니다~

Copy link
Collaborator

Choose a reason for hiding this comment

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

요부분은 제가 지워 놓겠습니다

<main>
{/* 맞춤 공고 */}
{!search && (
<article className="bg-red-10 px-32 py-60">
Copy link
Contributor

Choose a reason for hiding this comment

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

❗ mobile 일 때는 padding 값이 다릅니다~

Copy link
Author

Choose a reason for hiding this comment

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

네 알겠습니다

Copy link
Collaborator

Choose a reason for hiding this comment

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

디자인은 다시해서 변경 되었습니다!

Comment on lines 131 to 134
<span className="text-h1 font-bold text-primary">
{search}
</span>
<span className="text-h1 font-bold">에 대한 공고 목록</span>
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
<span className="text-h1 font-bold text-primary">
{search}
</span>
<span className="text-h1 font-bold">에 대한 공고 목록</span>
<span className="text-primary">
{search}
</span>
대한 공고 목록

💬 이런식으로도 작성 가능할 것 같습니다~ 기본적으로 font 설정들은 상속이 되기 때문에 상위 태그에서 지정해주면 하위에서는 설정하지 않을 수도 있습니다

Copy link
Author

Choose a reason for hiding this comment

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

네 알겠습니다!

Comment on lines 155 to 157
{appliedFilterCount > 0 && (
<span className="ml-2">({appliedFilterCount})</span>
)}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
{appliedFilterCount > 0 && (
<span className="ml-2">({appliedFilterCount})</span>
)}
{appliedFilterCount > 0 && (
' ' + appliedFilterCount
)}

💬 이런 식으로도 가능하지 않을까요?

Copy link
Author

Choose a reason for hiding this comment

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

간결하고 더 좋네요 리펙토링때 수정하겠습니다!

Copy link
Collaborator

@Yun-Jinwoo Yun-Jinwoo left a comment

Choose a reason for hiding this comment

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

고생하셨습니다~
주영님 피드백들을 모두 반영하면 좋겠지만, 동작에 이상이 없다면 우선 이대로 merge해서 테스트 시작하고 추후에 fix하는게 나을 것 같습니다. 시간이 없어서..!

@Yun-Jinwoo
Copy link
Collaborator

Yun-Jinwoo commented Jun 20, 2025

➕ 빌드 오류가 발생하네요~ vscode에서 npm run build 입력하셔서 나오는 오류에 대해 수정 부탁드립니다~

@Moon-ju-young
Copy link
Contributor

빌드 오류는 최신 사항 반영이 안되어서 그런 것 같습니다!

@minimo-9 minimo-9 requested review from Moon-ju-young, Yun-Jinwoo and minimo-9 and removed request for Yun-Jinwoo June 21, 2025 17:17
@minimo-9 minimo-9 assigned minimo-9 and unassigned minimo-9 Jun 21, 2025
@minimo-9 minimo-9 assigned ghost Jun 21, 2025
@minimo-9 minimo-9 added the ✨ 기능 추가/구현 새로운 기능을 추가하거나 구현했어요! label Jun 21, 2025
Copy link
Contributor

@Moon-ju-young Moon-ju-young left a comment

Choose a reason for hiding this comment

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

수고하셨습니다~ 앞선 코멘트에 대해서도 답변해주시면 감사하겠습니다! 그리고 최대한 pr/branch를 나눠서 작업해주시는 게 좋을 것 같습니다~ 건드리는 파일이 많네요

Copy link
Contributor

Choose a reason for hiding this comment

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

❓ filter 내에서 시간을 바꿨다가 돌려놓는 이유가 뭘까요?

Copy link
Collaborator

Choose a reason for hiding this comment

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

필터를 기준으로 바뀌어야하는지 몰랐습니다 ㅠ

Comment on lines 119 to 142
useEffect(() => {
if (!recommendedNotices.length || !scrollRef.current) return;

const container = scrollRef.current;

const interval = setInterval(() => {
const card = container.querySelector('[data-card]'); // 첫 번째 카드
if (!card) return;

const cardWidth = card.getBoundingClientRect().width; // getBoundingClientRect 브라우저에 실제 카드 너비를 가져옴
const style = window.getComputedStyle(container); // getComputedStyle 적용된 css 찾기
const gap = parseInt(style.columnGap || style.gap || '0', 10);

const scrollAmount = container.scrollLeft + cardWidth + gap; // 카드 1개 너비 + 간격만큼 이동 거리 계산
const maxScrollLeft = container.scrollWidth - container.clientWidth; // 최대 거리 계산 / scrollWidth 총 길이 / clientWidth 보이는 너비

if (scrollAmount >= maxScrollLeft - 1) {
container.scrollTo({ left: 0, behavior: 'smooth' }); // 처음으로 이동
} else {
container.scrollTo({ left: scrollAmount, behavior: 'smooth' }); // 현 위치에서 이동
}
}, 4000);
return () => clearInterval(interval);
}, [recommendedNotices]);
Copy link
Contributor

Choose a reason for hiding this comment

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

❓ 어떤 기능을 하는 부분일까요? 혹시 아래와 같이 알아서 맞춤 공고가 이동하는 부분일까요?

-.Clipchamp.mp4

Copy link
Collaborator

Choose a reason for hiding this comment

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

넵 맞습니다 자동 이동입니다!

Copy link
Contributor

Choose a reason for hiding this comment

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

필요한 기능은 아닌 것 같습니다 사용자가 스크롤 하는데도 이동하여 오히려 불편하다는 느낌이 드네요~

@Moon-ju-young Moon-ju-young changed the title 공고리스트 구현 ✨ feat: 공고리스트 구현 Jun 21, 2025
@minimo-9 minimo-9 merged commit 6cbd9a9 into develop Jun 22, 2025
2 checks passed
@minimo-9 minimo-9 deleted the feat/noticeList branch June 22, 2025 12:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ 기능 추가/구현 새로운 기능을 추가하거나 구현했어요!

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] 공고 리스트 페이지 구현

3 participants