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

모임카드 API 연결 #976

Merged
merged 4 commits into from
Jan 23, 2025
Merged

모임카드 API 연결 #976

merged 4 commits into from
Jan 23, 2025

Conversation

j-nary
Copy link
Member

@j-nary j-nary commented Jan 13, 2025

🚩 관련 이슈

📋 작업 내용

  • 모임카드 API 연결

📌 PR Point

서버사이드로 호출 시 token 누락 문제

  • useQuery 사용했을 땐 잘 되는데, getStaticProps 를 사용하니 token이 누락된다.

  • api는 아래와 같다.

    type RecommendMeetingListResponse =
      paths['/meeting/v2/recommend']['get']['responses']['200']['content']['application/json;charset=UTF-8'];
    export const getRecommendMeetingList = async ({ meetingIds = [] }: { meetingIds: number[] }) => {
      const meetingIdsParams = meetingIds.reduce((acc, id, idx) => {
        return acc + (idx === 0 ? '?' : '&') + `meetingIds=${id}`;
      }, '');
      return (await api.get<RecommendMeetingListResponse>(`/meeting/v2/recommend${meetingIdsParams}`, {})).data.meetings;
    };
  • 이유가 뭘까? token이 어떻게 들어가는지 확인하기 위해 api 인스턴스를 확인해보았다.

    import { crewToken, playgroundToken } from '@/stores/tokenStore';
    
    const baseURL =
      process.env.NEXT_PUBLIC_APP_ENV === 'production' ? 'https://crew.api.prod.sopt.org' : 'https://crew.api.dev.sopt.org';
    
    const playgroundBaseURL =
      process.env.NEXT_PUBLIC_APP_ENV === 'production'
        ? 'https://playground.api.sopt.org/'
        : 'https://playground.dev.sopt.org/';
    
    export const api = axios.create({
      baseURL,
    });
    
    crewToken.subscribe(newToken => {
      api.defaults.headers.common['Authorization'] = `Bearer ${newToken}`;
    });
  • crewToken.subscribe 로 token을 지정해주는 걸 알았다.

    • 이건 런타임에 작동하는 건가?
    • @/stores/tokenStore 는 클라이언트 전용 상태 관리 라이브러리였다.
    • 결과적으로, 서버 환경에서는 Authorization 헤더가 반영되지 않는 것!
  • 어떻게 해결해야할까?

    1. 서버 환경에서 토큰 명시적으로 전달
      • crewToken이 뭔지 알고? playgroundToken이랑도 엮여있어서 어려울 것 같다.
    2. 서버 환경에서도 접근 가능한 방식으로 토큰 관리
      • 환경변수나 쿠키에 저장하도록 변경
      • Next auth 이용 (매우 큰 리소스)
  • RecommendMeetingList 는 기획에서 정해주는 모임 id를 수동으로 설정하는 방식이기 때문에 정적 데이터여서 서버사이드 호출을 활용하고 싶었는데… 서버 환경에 토큰을 명시적으로 전달하기가 힘든 상황인 것 같다.

    • React Query를 사용하는 방법밖에 없을까?
    • 그럼 모든 api를 서버사이드에서 호출할 수 없다는 이야기가 되는데 ..
  • 토큰을 현재 어떻게 가져오고 있지?

    // src/stores/tokenStore.ts
    import { atom } from 'nanostores';
    export const crewToken = atom<string | undefined>();
    • Recoil을 SSR, SSG 같은 서버 환경에서 사용할 수 없나?

    • Recoil Hydration 이라는게 있대

    • 애초에 crewToken 을 가져오기 위해선 playgroundToken 이 필요하고, playgroundTokenlocalStorage 즉, 웹 스토리지를 사용한다. 하지만, Next.js 서버에는 웹 스토리지가 없다.

    export const getPlaygroundToken = () => {
      return localStorage.getItem(ACCESS_TOKEN_KEY);
    };
    
    export const getCrewServiceToken = async (playgroundToken: string) => {
      const { accessToken: crewToken } = await getCrewToken(playgroundToken);
      return crewToken;
    };
    
    export const setAccessTokens = async () => {
      const _playgroundToken = getPlaygroundToken();
      const _crewToken = await getCrewServiceToken(_playgroundToken);
      
      // recoil 저장
      crewToken.set(_crewToken);
      playgroundToken.set(_playgroundToken);
    };
  • playgroundToken 을 서버환경에서 받을 수 있는 방법이 없을까?

    1. 두 레포가 공통으로 접근할 수 있는 데이터 저장소를 사용해서 토큰을 저장하고 공유
    2. 쿠키를 사용한 전달
    3. 환경 변수 또는 Config 파일 공유 (모노레포의 루트 디렉토리에서)
  • playgroundToken 이 쿠키를 통해 전달되는데 가장 리소스가 적게 들 것 같다.

    • 이에 대해 다른 분들은 어떻게 생각하시는지 궁금합니다. 우선 useQuery로 구현해두었습니다.
    • 하지만, 이후의 작업을 위해서라도 초기 로딩 속도 개선 등을 위해 Next.js의 서버사이드 렌더링을 활용하면 좋을 것 같다는 생각이 들어서요!
    • 이를 위해 플그팀의 협력이 필요해보이는데, 그 전에 크루 프론트엔드 분들의 의견을 듣고 싶습니다 😊

📸 스크린샷

image

Copy link

height bot commented Jan 13, 2025

Link Height tasks by mentioning a task ID in the pull request title or commit messages, or description and comments with the keyword link (e.g. "Link T-123").

💡Tip: You can also use "Close T-X" to automatically close a task when the pull request is merged.

@borimong
Copy link
Contributor

빌드에러(타입에러) 로 배포 실패했네요!! 타입 에러 먼저 잡아주실 수 있으실까요~??

Copy link
Contributor

@ocahs9 ocahs9 left a comment

Choose a reason for hiding this comment

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

쿠키에 저장하는 방식 좋네요! 플그팀이랑 논의해볼만 한 것 같아요.
만약 논의 결과 쿠키로 플그 토큰을 쿠키에 저장하게 된다면

export async function getServerSideProps(context: GetServerSidePropsContext) {
  const cookies = context.req.headers.cookie;
  const playgroundToken = cookies
    ?.split('; ')
    ?.find((cookie) => cookie.startsWith('playgroundToken='))
    ?.split('=')[1];
  
  if (!playgroundToken) {
    return { notFound: true };
  }

  const crewToken = await getCrewServiceToken(playgroundToken);

  return {
    props: { crewToken },
  };
}

와 같은 방식으로 사용�하면 될 거 같아요. 제가 간단하게 작성해본 코드라서 문제가 있을 수 있는데 간단하게 설명하면 context는 Next.js의 데이터 페칭 함수에 첫번째 인자에 제공되는 객체고, HTTP 요청의 정보(쿼리, 헤더, 쿠키 등)를 다룹니다 (이벤트 핸들러의 이벤트 객체와 다름 - 브라우저 이벤트 객체) 그래서 해당 쿠키가 존재하는지 확인하고 페이지 컴포넌트에 넘겨주는 로직을 작성하면 좋을 것 같아요

(이렇게 코드를 작성해두고 적용하려면 그 전에 로그인 시 플그 토큰을 set하는 로직도 필요할 것으로 예상합니다) -> document.cookie = playgroundToken=${token}; Path=/; HttpOnly; Secure; SameSite=Strict;

여튼 나중에 논의 후에 작성해야 할 코드도 더 고민해보면 좋을 것 같아요 .
좋은 의견 감사합니다!

import CardList from '@components/page/home/HomeCardList/CardList';
import { styled } from 'stitches.config';

const HomeCardList = ({ groupBrowsingCardData }: { groupBrowsingCardData: GroupBrowsingCardResponse }) => {
const HomeCardList = ({ groupBrowsingCardData }: { groupBrowsingCardData: RecommendMeetingListQueryResponse }) => {
const { data: recommendMeetings } = useGetRecommendMeetingListQuery({ meetingIds: [359, 360, 361] });
Copy link
Contributor

Choose a reason for hiding this comment

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

아 이렇게 고정되니까 미리 서버사이드에서 렌더링해오는 걸 원했던거군요..!
생산적인 논의가 될 거 같은데, (성능 개선 vs 작업을 위한 리소스) 를 주제로 플그팀이랑 잘 얘기보는 것도 좋을 것 같아요!
전 리소스가 조금 들더라도 플그팀이랑 논의해보는 것에 찬성합니다!

paths['/meeting/v2/recommend']['get']['responses']['200']['content']['application/json;charset=UTF-8']['meetings'];
export const useGetRecommendMeetingListQuery = ({ meetingIds = [] }: { meetingIds: number[] }) => {
return useQuery<RecommendMeetingListQueryResponse>({
queryKey: ['getRecommendMeetingList', ...meetingIds],
Copy link
Contributor

Choose a reason for hiding this comment

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

혹시 쿼리키로 ...mettingIds (고정된 3개의 아이디) 도 같이 사용한 특별한 이유가 있나요?
그냥 'getRecommendMeetingList' 만 있어도 상관없지 않나 싶어서요!

Copy link
Member Author

Choose a reason for hiding this comment

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

기획이 그렇슴다 ..! 고정된 3개 아이디 기획쪽에서 얼라인 내려주는 형태에요!

Comment on lines +55 to +57
const meetingIdsParams = meetingIds.reduce((acc, id, idx) => {
return acc + (idx === 0 ? '?' : '&') + `meetingIds=${id}`;
}, '');
Copy link
Contributor

Choose a reason for hiding this comment

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

reduce 적절하게 잘 활용하셨네요! 알고리즘 잘 짠다..

@j-nary
Copy link
Member Author

j-nary commented Jan 23, 2025

빌드에러(타입에러) 로 배포 실패했네요!! 타입 에러 먼저 잡아주실 수 있으실까요~??

isCoLeader 필드가 갑자기 서버 response에서 누락돼서 그렇습니다!
서버 측에서 반영 완료했으니 다시 검토해주시면 감사합니다 :)

@j-nary j-nary merged commit d3d2556 into develop Jan 23, 2025
1 check passed
@j-nary j-nary deleted the feat/#975 branch January 23, 2025 23:21
@j-nary j-nary mentioned this pull request Jan 24, 2025
5 tasks
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.

모임카드 API 연결
3 participants