Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FE] 회원 행사 생성, 비회원 행사 생성 구현 #835

Merged
merged 26 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8312987
feat: 이벤트 생성에서 반복적으로 사용되는 타입 선언
pakxe Nov 19, 2024
ade52ec
refactor: 반복되는 path를 상수로 만들고 이를 재사용함
pakxe Nov 19, 2024
4b6eeae
feat: 게스트 행사, 회원 행사 생성을 위한 api 연결 함수 구현
pakxe Nov 19, 2024
13695a6
feat: 유저 이름이 올바르지 않을 때 보여줄 에러 메세지 수정
pakxe Nov 19, 2024
159357d
refactor: 이미 제작된 이름 유효성 검증 로직을 재사용하도록 수정
pakxe Nov 19, 2024
762e565
feat: 행사 생성 시 관리자 이름을 입력받는 페이지 구현
pakxe Nov 19, 2024
2a7b518
feat: 회원 이벤트 생성 과정 퍼널 구현
pakxe Nov 19, 2024
968f71a
refactor: 불필요한 조건문 제거
pakxe Nov 19, 2024
c175e5b
feat: 비회원 행사 생성 페이지, 회원 행사 생성 페이지를 라우터에 연결
pakxe Nov 19, 2024
63f21e1
refactor: 행사 생성시 사용되는 타입으로 재사용하도록 수정
pakxe Nov 19, 2024
2193639
feat: 회원 이벤트 생성을 트래킹할 amplitude 함수 구현
pakxe Nov 19, 2024
6016b66
feat: 회원 이벤트 생성 시 행사명만 받으면 바로 행사를 생성하는 플로우로 스텝 페이지 구현
pakxe Nov 19, 2024
d4f71c7
chore: 행사 생성 과정이 회원 여부로 분기됨에 따라 위치와 파일명 수정
pakxe Nov 19, 2024
01ffe9a
feat: 행사 생성 시 행사 관리자의 이름을 입력받는 스텝 페이지에서 사용하는 이름 검증 훅 구현
pakxe Nov 19, 2024
61ee956
chore: 행사 생성에 대한 path 경로를 수정
pakxe Nov 19, 2024
26c3227
chore: 사용하지 않게된 파일 제거
pakxe Nov 19, 2024
5c5abdf
test: 분리 개발로 인해 테스트 수행 불가하므로 테스트코드 주석처리
pakxe Nov 19, 2024
db26421
rename: nickName -> nickname으로 수정
pakxe Nov 19, 2024
910983e
feat: 사용하지 않는 return 함수 제거
pakxe Nov 19, 2024
230d8ab
feat: 회원 이벤트 생성 완료 버튼을 누를 때 로딩이 되도록 함
pakxe Nov 19, 2024
f132106
rename: submit->onSubmit으로 form태그에서 사용되는 핸들러이름과 일치시킴
pakxe Nov 19, 2024
c95fcd2
feat: 행사 생성 시 입력받는 관리자 이름, 비밀번호 스텝 페이지의 문구 변경
pakxe Nov 19, 2024
f66c76d
rename: Page -> Step으로 이름 변경
pakxe Nov 19, 2024
743ad77
feat: api에서 사용되는 타입을 serviceType으로 이동
pakxe Nov 19, 2024
4cde069
chore: 충돌 해결과 병합
pakxe Nov 20, 2024
c571bd9
rename: NickName -> Nickname으로 수정
pakxe Nov 20, 2024
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
124 changes: 56 additions & 68 deletions client/cypress/e2e/createEvent.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,72 +8,60 @@ beforeEach(() => {
});

describe('Flow: 랜딩 페이지에서부터 이벤트를 생성 완료하는 flow', () => {
it('랜딩페이지에서 "정산 시작하기" 버튼을 눌러 행사 이름 입력 페이지로 이동해야 한다.', () => {
cy.visit('/');
cy.get('button').contains('정산 시작하기').click();
cy.url().should('include', ROUTER_URLS.createEvent);
});

context('행사 이름 입력 페이지', () => {
beforeEach(() => {
cy.visit(ROUTER_URLS.createEvent);
});

it('행사 이름 입력 페이지에서 input이 포커싱 되어 있고, "다음" 버튼이 비활성화 되어 있어야 한다.', () => {
cy.get('input').focused();
cy.get('button').contains('다음').should('have.attr', 'disabled');
});

it('행사 이름이 1자 이상 입력된 경우 "다음" 버튼이 활성화 되고, 값이 없는 경우 "다음" 버튼이 비활성화 되어야 한다.', () => {
cy.get('input').type(CONSTANTS.eventName);
cy.get('button').contains('다음').should('not.have.attr', 'disabled');
cy.get('input').clear();
cy.get('input').should('have.value', '');
cy.get('button').contains('다음').should('have.attr', 'disabled');
});

it('행사 이름을 입력한 후 "다음" 버튼을 누르면 행사 비밀번호 설정 화면으로 이동해야 한다.', () => {
cy.get('input').type(CONSTANTS.eventName);
cy.get('button').contains('다음').click();

// 다음 버튼을 클릭하면 /create/event 경로가 아니라 /create/event/?로 가네요.. 그래서 일단 제거함.
cy.contains('비밀번호').should('exist');
});
});

context('행사 비밀번호 입력 페이지', () => {
beforeEach(() => {
cy.createEventName(CONSTANTS.eventName);
});

it('행사 비밀번호 입력 페이지에서 input이 포커싱 되어 있고, "행동 개시!" 버튼이 비활성화 되어 있어야 한다.', () => {
cy.get('input').focused();
cy.get('button').contains('행동 개시!').should('have.attr', 'disabled');
});

it('행사 비밀번호에 숫자가 아닌 입력을 할 경우 값이 입력되지 않아야 한다.', () => {
cy.get('input').type('테스트');
cy.get('input').should('have.value', '');
});

it(`행사 비밀번호에 ${RULE.maxEventPasswordLength}자리 이상 입력을 할 경우 처음 ${RULE.maxEventPasswordLength}자리만 입력되어야 한다.`, () => {
cy.get('input').type('12345');
cy.get('input').should('have.value', CONSTANTS.eventPassword);
});

it('행사 비밀번호이 1자 이상 입력된 경우 "행동 개시!" 버튼이 활성화 되고, 값이 없는 경우 "행동 개시!" 버튼이 비활성화 되어야 한다.', () => {
cy.get('input').type(CONSTANTS.eventPassword);
cy.get('button').contains('행동 개시!').should('not.have.attr', 'disabled');
cy.get('input').clear();
cy.get('input').should('have.value', '');
cy.get('button').contains('행동 개시!').should('have.attr', 'disabled');
});

it('행사 비밀번호을 입력한 후 "행동 개시!" 버튼을 누르면 행사 생성 완료 화면으로 이동해야 한다.', () => {
cy.interceptAPI({type: 'postEvent', statusCode: 200});
cy.interceptAPI({type: 'getEventName', statusCode: 200});
cy.get('input').type(CONSTANTS.eventPassword);
cy.get('button').contains('행동 개시!').click();
});
});
// it('랜딩페이지에서 "정산 시작하기" 버튼을 눌러 행사 이름 입력 페이지로 이동해야 한다.', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

cypress를 주석처리하셨군요ㅋㅋ 테스트 보다는 구현이 먼저라 생각하기 때문에 정상적으로 돌아간다는 전제 하에 임시로 주석처리 괜찮은 것 같아요

Copy link
Contributor

Choose a reason for hiding this comment

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

@jinhokim98 이 부분은 쿠키가 작성한 부분이 반영되면 해결되는것인가요??

Copy link
Contributor Author

Choose a reason for hiding this comment

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

이 이슈 하나만 있다면 구현했을텐데, 로그인에서 해야할 이슈가 좀 많아서 임시로 해두었습니다 ㅠ.ㅠ

Copy link
Contributor Author

@pakxe pakxe Nov 19, 2024

Choose a reason for hiding this comment

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

쿠키 기능이 완성되면 회원과 비회원 각각의 e2e 테스트를 모두 작성해야 할 것 같네요 🙋‍♀️ 다만, 로그인과 행사 생성 플로우보다 프로젝트의 핵심 기능에 우선적으로 테스트를 작성하는 것이 더 효율적일 수도 있다고 생각합니다. (애초에 지금 e2e가 행사 생성 과정 말고는 없어서.. ㅜㅜ) 취준으로 인해 프로젝트에 사용할 시간이 제한적인 만큼, 무엇을 위해 테스트를 작성하는지를 명확히 정리한 후, 어떤 테스트가 더 중요한지 고민해보는 것이 좋을 것 같아요 👍

// cy.visit('/');
// cy.get('button').contains('정산 시작하기').click();
// cy.url().should('include', ROUTER_URLS.createGuestEvent);
// });
// context('행사 이름 입력 페이지', () => {
// beforeEach(() => {
// cy.visit(ROUTER_URLS.createGuestEvent);
// });
// it('행사 이름 입력 페이지에서 input이 포커싱 되어 있고, "다음" 버튼이 비활성화 되어 있어야 한다.', () => {
// cy.get('input').focused();
// cy.get('button').contains('다음').should('have.attr', 'disabled');
// });
// it('행사 이름이 1자 이상 입력된 경우 "다음" 버튼이 활성화 되고, 값이 없는 경우 "다음" 버튼이 비활성화 되어야 한다.', () => {
// cy.get('input').type(CONSTANTS.eventName);
// cy.get('button').contains('다음').should('not.have.attr', 'disabled');
// cy.get('input').clear();
// cy.get('input').should('have.value', '');
// cy.get('button').contains('다음').should('have.attr', 'disabled');
// });
// it('행사 이름을 입력한 후 "다음" 버튼을 누르면 행사 관리자 이름 입력 화면으로 이동해야 한다.', () => {
// cy.get('input').type(CONSTANTS.eventName);
// cy.get('button').contains('다음').click();
// cy.contains('관리자의 이름').should('exist');
// });
// });
// context('행사 비밀번호 입력 페이지', () => {
// beforeEach(() => {
// cy.createEventName(CONSTANTS.eventName);
// });
// it('행사 비밀번호 입력 페이지에서 input이 포커싱 되어 있고, "행동 개시!" 버튼이 비활성화 되어 있어야 한다.', () => {
// cy.get('input').focused();
// cy.get('button').contains('행동 개시!').should('have.attr', 'disabled');
// });
// it('행사 비밀번호에 숫자가 아닌 입력을 할 경우 값이 입력되지 않아야 한다.', () => {
// cy.get('input').type('테스트');
// cy.get('input').should('have.value', '');
// });
// it(`행사 비밀번호에 ${RULE.maxEventPasswordLength}자리 이상 입력을 할 경우 처음 ${RULE.maxEventPasswordLength}자리만 입력되어야 한다.`, () => {
// cy.get('input').type('12345');
// cy.get('input').should('have.value', CONSTANTS.eventPassword);
// });
// it('행사 비밀번호이 1자 이상 입력된 경우 "행동 개시!" 버튼이 활성화 되고, 값이 없는 경우 "행동 개시!" 버튼이 비활성화 되어야 한다.', () => {
// cy.get('input').type(CONSTANTS.eventPassword);
// cy.get('button').contains('행동 개시!').should('not.have.attr', 'disabled');
// cy.get('input').clear();
// cy.get('input').should('have.value', '');
// cy.get('button').contains('행동 개시!').should('have.attr', 'disabled');
// });
// it('행사 비밀번호을 입력한 후 "행동 개시!" 버튼을 누르면 행사 생성 완료 화면으로 이동해야 한다.', () => {
// cy.interceptAPI({type: 'postEvent', statusCode: 200});
// cy.interceptAPI({type: 'getEventName', statusCode: 200});
// cy.get('input').type(CONSTANTS.eventPassword);
// cy.get('button').contains('행동 개시!').click();
// });
// });
});
2 changes: 1 addition & 1 deletion client/cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Cypress.Commands.add('interceptAPI', ({type, delay = 0, statusCode = 200}: Inter
});

Cypress.Commands.add('createEventName', (eventName: string) => {
cy.visit(ROUTER_URLS.createEvent);
cy.visit(ROUTER_URLS.createMemberEvent);
Copy link
Contributor

Choose a reason for hiding this comment

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

이 부분 충돌 날 거예요. 제 부분에서 로그인 진입점도 있어서 이 코드 수정했거든요.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

예상했습니다 ㅎㅎ 즐거운 충돌쇼

cy.get('input').type(eventName);
cy.get('button').contains('다음').click();
});
Expand Down
18 changes: 11 additions & 7 deletions client/src/apis/request/event.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import {Event, EventId} from 'types/serviceType';
import {WithErrorHandlingStrategy} from '@errors/RequestGetError';
import {CreateEventArgs, EventName} from 'types/createEvent';

import {ADMIN_API_PREFIX, USER_API_PREFIX} from '@apis/endpointPrefix';
import {requestGet, requestPatch, requestPostWithResponse, requestPut} from '@apis/fetcher';
import {WithEventId} from '@apis/withId.type';

export interface RequestPostEvent {
eventName: string;
password: string;
}
export const requestPostGuestEvent = async (postEventArgs: CreateEventArgs) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

args라는 변수명 좋네요! props가 익숙해서 props로 지은 것도 많은데 함수의 파라미터이니깐 args가 더 좋은 이름인 것 같아요

Copy link
Contributor

Choose a reason for hiding this comment

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

api type 이름에 대해서 예ㅔㅔ전에 용어정리 하면서 함께 통일했던 기억이 있어요
https://pakxe.notion.site/8455260f33074f7fbf0fbdf55b187e2f?pvs=4
사실 최근들어서 다들 생각하지 않고 그냥 코드를 작성하고 있는것 같은데,
그때도 유지보수성을 위해서 모두가 같은 context에서 이해할 수 있도록 수정했는데, 최대한 맞추는게 이후에도 보기 좋고 이해하기 좋을 것이라고 생각돼요.

이전에 작업한 convention은 팀과의 협업 중 불편하다고 생각되어 만들어 진 것이니까요~!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

저는 컴포넌트의 인자로 들어가는 값이면 props, 함수의 인자로 들어가는 값이면 args라고 생각하며 구현했었어요!

return await requestPostWithResponse<EventId>({
endpoint: `${USER_API_PREFIX}/guest`,
body: {
...postEventArgs,
},
});
};

export const requestPostEvent = async ({eventName, password}: RequestPostEvent) => {
export const requestPostMemberEvent = async (eventName: EventName) => {
return await requestPostWithResponse<EventId>({
endpoint: USER_API_PREFIX,
endpoint: `${USER_API_PREFIX}/events`,
body: {
eventName,
password,
},
});
};
Expand Down
2 changes: 1 addition & 1 deletion client/src/constants/errorMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const SERVER_ERROR_MESSAGES: ErrorMessage = {
export const ERROR_MESSAGE = {
eventName: SERVER_ERROR_MESSAGES.EVENT_NAME_LENGTH_INVALID,
eventPasswordType: SERVER_ERROR_MESSAGES.EVENT_PASSWORD_FORMAT_INVALID,
memberName: SERVER_ERROR_MESSAGES.MEMBER_NAME_LENGTH_INVALID,
memberName: `이름은 ${RULE.maxMemberNameLength}자까지 입력 가능해요.`,
purchasePrice: `${RULE.maxPrice.toLocaleString('ko-kr')}원 이하의 숫자만 입력이 가능해요`,
purchaseTitle: `지출 이름은 ${RULE.maxBillNameLength}자 이하의 한글, 영어, 숫자만 가능해요`,
preventEmpty: '값은 비어있을 수 없어요',
Expand Down
29 changes: 17 additions & 12 deletions client/src/constants/routerUrls.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
const EVENT = '/event';
const EVENT_WITH_EVENT_ID = `${EVENT}/:eventId`;
Copy link
Contributor

Choose a reason for hiding this comment

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

event with event id 처음 들었을 때 어색하긴 했는데 어쩔 수 없는 것 같아요 id라고 줄이면 무슨 id인지 혼동할 수 있으니

Copy link
Contributor Author

Choose a reason for hiding this comment

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

흑흑... 더 생각해볼게요


export const ROUTER_URLS = {
main: '/',
createEvent: '/event/create',
event: '/event',
eventManage: '/event/:eventId/admin',
home: '/event/:eventId/home',
member: '/event/:eventId/admin/member',
addBill: '/event/:eventId/admin/add-bill',
editBill: '/event/:eventId/admin/edit-bill',
eventEdit: 'event/:eventId/admin/edit',
images: '/event/:eventId/images',
addImages: '/event/:eventId/admin/add-images',
send: 'event/:eventId/:memberId/send',
qrCode: 'event/:eventId/qrcode',
createGuestEvent: `${EVENT}/create/guest`,
createMemberEvent: `${EVENT}/create/member`,
eventManage: `${EVENT_WITH_EVENT_ID}/admin`,
home: `${EVENT_WITH_EVENT_ID}/home`,
member: `${EVENT_WITH_EVENT_ID}/admin/member`,
addBill: `${EVENT_WITH_EVENT_ID}/admin/add-bill`,
editBill: `${EVENT_WITH_EVENT_ID}/admin/edit-bill`,
eventEdit: `${EVENT_WITH_EVENT_ID}/admin/edit`,
images: `${EVENT_WITH_EVENT_ID}/images`,
addImages: `${EVENT_WITH_EVENT_ID}/admin/add-images`,
send: `${EVENT_WITH_EVENT_ID}/:memberId/send`,
qrCode: `${EVENT_WITH_EVENT_ID}/qrcode`,
event: EVENT,
login: '/login',
};
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import {useState} from 'react';

import useSetEventNameStep from './useSetEventNameStep';
import {useSetNickNameStep} from './useSetNicknameStep';

// 행사 생성 페이지에서 여러 스텝에 걸쳐 사용되는 상태를 선언해 내려주는 용도의 훅입니다.
const useCreateEventData = () => {
const useCreateGuestEventData = () => {
const eventNameProps = useSetEventNameStep();
const nickNameProps = useSetNickNameStep();
const [eventToken, setEventToken] = useState('');

return {
eventNameProps,
nickNameProps,
eventToken,
setEventToken,
};
};

export default useCreateEventData;
export default useCreateGuestEventData;
Original file line number Diff line number Diff line change
@@ -1,52 +1,46 @@
import {useState} from 'react';

import validateEventPassword from '@utils/validate/validateEventPassword';
import {CreateEventArgs, EventName} from 'types/createEvent';

import RULE from '@constants/rule';

import useRequestPostEvent from './queries/event/useRequestPostEvent';
import useAmplitude from './useAmplitude';
import useRequestPostGuestEvent from '../queries/event/useRequestPostGuestEvent';
import useAmplitude from '../useAmplitude';

export type UseSetEventPasswordStepReturnType = ReturnType<typeof useSetEventPasswordStep>;

type SubmitDataForPostEventArgs = Omit<CreateEventArgs, 'password'> & {
event: React.FormEvent<HTMLFormElement>;
setEventToken: (eventToken: string) => void;
};

const useSetEventPasswordStep = () => {
const [password, setPassword] = useState('');
const [errorMessage, setErrorMessage] = useState('');
const [canSubmit, setCanSubmit] = useState(false);
const {postEvent: requestPostEvent, isPostEventPending} = useRequestPostEvent();
const {postEvent: requestPostEvent, isPostEventPending} = useRequestPostGuestEvent();

const {trackCompleteCreateEvent} = useAmplitude();

const submitDataForPostEvent = async ({
event,
eventName,
setEventToken,
}: {
event: React.FormEvent<HTMLFormElement>;
eventName: string;
setEventToken: (eventToken: string) => void;
}) => {
const submitDataForPostEvent = async ({event, nickname, eventName, setEventToken}: SubmitDataForPostEventArgs) => {
event.preventDefault();

await postEvent(eventName, setEventToken);
};

const getPasswordWithPad = () => {
return String(password).padStart(4, '0');
};

const postEvent = async (eventName: string, updateEventToken: (eventToken: string) => void) => {
await requestPostEvent(
{eventName, password: getPasswordWithPad()},
{eventName, nickname, password: getPasswordWithPad()},
{
onSuccess: ({eventId}) => {
trackCompleteCreateEvent({eventName, eventToken: eventId});
updateEventToken(eventId);
setEventToken(eventId);
},
},
);
};

const getPasswordWithPad = () => {
return String(password).padStart(4, '0');
};

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const newValue = event.target.value;
const validation = validateEventPassword(newValue);
Expand Down
29 changes: 29 additions & 0 deletions client/src/hooks/createEvent/useSetNicknameStep.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {useState} from 'react';

import validateMemberName from '@utils/validate/validateMemberName';
import {NickName} from 'types/createEvent';

type UseSetNicknameStepProps = ReturnType<typeof useSetNicknameStep>;
Copy link
Contributor

Choose a reason for hiding this comment

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

ReturnType 좋아요! 훅의 리턴이 너무 많을 때 이렇게 쓰면 유용하더라구요

Copy link
Contributor Author

Choose a reason for hiding this comment

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

마자요. 자동 타입 추론을 최대한 활용하는게 이득이죵


const useSetNicknameStep = () => {
const [nickname, setNickname] = useState<NickName>('');
const [errorMessage, setErrorMessage] = useState<string | null>(null);
Copy link
Contributor

Choose a reason for hiding this comment

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

에러 없을 때 null 처리 좋습니다~

const [canSubmit, setCanSubmit] = useState(false);

const handleNicknameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const name = event.target.value;
const {isValid, errorMessage: errorMessageResult} = validateMemberName(name);

setErrorMessage(errorMessageResult);

if (isValid) {
setNickname(name);
}

setCanSubmit(name.length !== 0);
};

return {handleNicknameChange, canSubmit, nickname, errorMessage};
};

export {useSetNicknameStep, type UseSetNicknameStepProps};
25 changes: 25 additions & 0 deletions client/src/hooks/queries/event/useRequestPostGuestEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {useMutation, useQueryClient} from '@tanstack/react-query';

import {requestPostGuestEvent} from '@apis/request/event';
import {CreateEventArgs} from 'types/createEvent';

const useRequestPostGuestEvent = () => {
const queryClient = useQueryClient();

const {mutate, mutateAsync, ...rest} = useMutation({
Copy link
Contributor

Choose a reason for hiding this comment

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

여기서 mutate는 어디서 사용하나요?

Copy link
Contributor Author

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.

아하 죠스입니다~~

mutationFn: ({eventName, nickname, password}: CreateEventArgs) =>
requestPostGuestEvent({eventName, nickname, password}),
onSuccess: () => {
queryClient.removeQueries();
},
});

// 실행 순서를 await으로 보장하기 위해 mutateAsync 사용
return {
postEvent: mutateAsync,
isPostEventPending: rest.isPending,
Copy link
Contributor

Choose a reason for hiding this comment

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

pending을 내보내주는 이유는 버튼의 로딩 애니메이션을 보여주기 위한 것일까요?

Copy link
Contributor Author

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.

굳뜨~~

...rest,
};
};

export default useRequestPostGuestEvent;
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import {useMutation, useQueryClient} from '@tanstack/react-query';

import {RequestPostEvent, requestPostEvent} from '@apis/request/event';
import {requestPostMemberEvent} from '@apis/request/event';
import {CreateEventArgs, EventName} from 'types/createEvent';

import QUERY_KEYS from '@constants/queryKeys';

const useRequestPostEvent = () => {
const useRequestPostMemberEvent = () => {
const queryClient = useQueryClient();

const {mutate, mutateAsync, ...rest} = useMutation({
mutationFn: ({eventName, password}: RequestPostEvent) => requestPostEvent({eventName, password}),
mutationFn: (eventName: EventName) => requestPostMemberEvent(eventName),
Copy link
Contributor

Choose a reason for hiding this comment

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

이부분도 위에서 말했던, API type에 대한 convention을 지키는게 좋을 것 같습니당

Copy link
Contributor Author

@pakxe pakxe Nov 19, 2024

Choose a reason for hiding this comment

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

serviceType파일에 api관련된 타입을 적으라는 의미가 맞을까요? 토달스?! 이렇게 수정해봤습니당.

export type EventName = string;
export type NickName = string;
export type Password = string;

export interface EventCreationData {
  eventName: EventName;
  nickname: NickName;
  password: Password;
}

// 원래 있던거.
export interface Event {
  eventName: EventName;
  bankName: string;
  accountNumber: string;
}

Event라는 타입은 원래 있던 타입인데, 이벤트에 왜 계좌이름과 계좌번호라는 타입이 붙어있는진 잘 모르겠으나, 의도한 게 있을 것 같아 남겨뒀어요. 그래서 이벤트 생성시에 반복적으로 사용되는 타입인 행사명, 닉네임, 비밀번호를 담아 EventCreationDara라고 만들어봤습니다.

Copy link
Contributor

Choose a reason for hiding this comment

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

저희가 그때 의논한 바로는, src/apis/request 에서는 requst + post + 요청하는 method
이런식으로 네임을 지었던 것으로 기억해요. 백엔드 측에서도 나름 RESTful하게 API들을 수정했고, 저희도 그 컨벤션에 비슷하게 맞추면
한 페이지에서 이해가 깔끔하게 되는게 그 취지였구요!

다른 src/apis/request 파일들을 확인해 보시면, requestDeleteMember, requestPostBill 과 같이 작성된 것을 확인할 수 있을거에요~!!

onSuccess: () => {
queryClient.removeQueries();
},
Expand All @@ -22,4 +21,4 @@ const useRequestPostEvent = () => {
};
};

export default useRequestPostEvent;
export default useRequestPostMemberEvent;
Loading
Loading