[Workspace 구현 여정기 -3] workspace 이름 변경과 MongoDB에 저장하기

hyonun·2025년 1월 6일
0

Nocta - Workspace 구현

목록 보기
3/4

🚄 Workspace의 이름 변경

기본 워크스페이스 이름으로 undefined, workspace, 유저이름 등을 고민했지만 결국 유저에게 workspace이름을 바꾸게 하는게 더 UX상 좋다고 판단했습니다.

워크스페이스를 제작하며 owner의 역할로 어떤걸 부여할까 고민했습니다.

owner의 역할은 현재 초대기능만 있습니다.

하지만 workspace이름들이 유저a 라고한다면, a의 workspace로 통일되며 생성되고있었습니다.

그래서 owner의 경우, 자신의 workspace위에 마우스를 hover하면 연필 이모티콘이 나와서

workspace의 이름을 변경하도록 했습니다.

  • 안내 모달은 modal 에서 renameModal 로 확장하여 사용하였습니다.
  • API 는 workspace/rename 로 사용하였습니다.

서버는 workspace/rename API를 통해

  const handleRename = (newName: string) => {
    socket?.emit("workspace/rename", {
      workspaceId: id,
      newName,
    });
  };

workspaceId와 newName을 수신받습니다.


  @SubscribeMessage("workspace/rename")
  async handleWorkspaceRename(
    @MessageBody() data: { workspaceId: string; newName: string },
    @ConnectedSocket() client: Socket,
  ): Promise<void> {
    try {
      const workspace = await this.workSpaceService.getWorkspace(data.workspaceId);

      const userRole = await this.workSpaceService.getUserRole(
        client.data.userId,
        data.workspaceId,
      );
      if (userRole !== "owner") {
        throw new WsException("권한이 없습니다.");
      }

      // 워크스페이스 이름 업데이트
      await this.workSpaceService.updateWorkspaceName(data.workspaceId, data.newName);

      const members = await this.workSpaceService.getWorkspaceMembers(data.workspaceId);
      // 워크스페이스 멤버들에게 변경 알림
      const server = this.workSpaceService.getServer();
      for (const memberId of members) {
        const memberWorkspaces = await this.workSpaceService.getUserWorkspaces(memberId);
        server.to(`user:${memberId}`).emit("workspace/list", memberWorkspaces);
      }
    } catch (error) {
      throw new WsException(`워크스페이스 이름 변경 실패: ${error.message}`);
    }
  }

수신받은 workspaceId로 이름을 업데이트 해주고 해당 워크스페이스 맴버들에게만 새로 갱신한 workspace정보를 알립니다.

🕵️‍♀️유저 권한

기본적으로 유저는 3개를 생각하였지만 2개만 구현하였습니다.

owner와 editor가 핵심기능을 확인할 수 있는 유저 권한이라고 판단했습니다.

  1. owner
    • 워크스페이스를 처음 생선한사람에게 부여되는 권한이다.
    • 다른 유저를 초대할 수 있다.
    • +페이지를 전부 삭제한다. - 추가예정
  2. editor
    • 다른 유저에게 초대를 받아 편집을 할 수 있는 권한이다.
    • 다른 유저를 초대할 수 없다.
  3. viewer *미구현
    • 조회만 가능하다.

서버인스턴스 ↔ 몽고DB연동

이 부분에도 에러가 많이 발생했습니다. 특히 몽고DB스키마 데이터 타입에서 문제가 많았는데요.

기존의 workspaceId에 해당하는 mongoDB에 새롭게 배열을 넣어줘야했습니다.

이와 관련된 명령어를 찾다가 addToSet 이라는 명령어를 알게 되었습니다.

      await this.userModel.updateOne({ id: invitedUserId }, { $push: { workspaces: workspaceId } });

일반적으로 $push 를 사용하면 workspaces 라는 배열에 값을 넣게 됩니다.

계속 $push를 사용하면 값이 추가가 되겠죠.

      await this.userModel.updateOne(
        { id: invitedUserId },
        { $addToSet: { workspaces: workspaceId } },
      );

하지만 이 $addToSet을 사용하면 배열에 중복된 값이 있으면 넣지 않게됩니다.

그러니 key값에 원하는 정보를 update할때 유용하게 쓰일 수 있습니다.

또한 rename의 경우 바로 이름변경이 일어날때마다 mongoDB의 workspace 이름도 바꾸게 해주었습니다.

현재는 30분 단위로 server ↔ MongoDB의 저장이 일어나고 있습니다.

서버 인스턴스가 문제가 발생하면 30분어치의 정보가 날아가게 되는데 이 부분은 리팩토링 때 개선할 예정입니다.

  • 저장 단위를 더 나누기 (5분, 10분 등등)
  • [Redis 메모리 상태 백업]
    • Redis와 같은 인메모리 데이터베이스를 보조적으로 활용
  • [Replication 복제]
    • 서버를 2개이상으로 만들어 안전성을 부여한다.
  • [Incremental Backup 증분백업]
    • 서버의 로컬스토리지에 자주 저장하게 하고 xx분단위로 MongoDB에 저장하게 한다.

등등의 방법을 고려중에 있습니다.

아마 서버 개수를 늘리기 보단, Redis등을 활용할 예정입니다.

MongoDB에 저장되는 모습

위와 같은 형태로

workspace의 이름,

접근가능한 유저Id와 권한,

워크스페이스의 id,

워크스페이스 내부의 pageList를 배열 형태로 관리하고 있습니다.


👨‍✈️ 워크스페이스 변경은?

생각보다 어려웠던건 백엔드부분과 API부분보다는

어떤 방식으로 workspace에서 수신한 화면을 보여줄래? 였습니다.

저는 App.tsx에서

  const { isErrorModalOpen, errorMessage } = useErrorStore();
  const { userId } = useUserInfo();

  useEffect(() => {
    const socketStore = useSocketStore.getState();
    const savedWorkspace = sessionStorage.getItem("currentWorkspace");
    const workspaceId = savedWorkspace ? JSON.parse(savedWorkspace).id : null;
    socketStore.init(userId, workspaceId);

    return () => {
      setTimeout(() => {
        socketStore.cleanup();
      }, 0);
    };
  }, [userId]);

위와 같이 세션스토리지에서 workspaceId정보를 받아오고 이를 기반으로 서버와 통신하여 정보를 불러오게 했습니다.

그 이유는 새로고침을 실행하면 workspace정보가 날아가버리는 문제가 발생했습니다.

실제 우리가 이용하는 사이트도 새로고침하면 유저 로그인 정보가 남아있기 때문에 세션스토리지에 저장하였습니다.


🤗 마무리

이처럼 socket기능을 활용하여 workspace별 접속을 구현해보았습니다.

더욱 복잡한 형태로 로그인와 컴포넌트 간의 상호 작용이 일어난다면, searchParams과 같이 ReactRouter DOM을 사용할 것 같습니다.

이를 통해 뒤로가기, 앞으로 가기를 할때 View 상태를 유지할 것 같습니다.

현재는 완전 100% CSR이라서 진행하지 않았습니다만, 추후 workspace 이외에 화면이 생긴다면 추가할 것 같습니다.

이상으로 workspace 구현을 마치겠습니다.

감사합니다!!

profile
비전공자 + 반도체 경력2년의 IT 개발자 도전기~

0개의 댓글

관련 채용 정보