오전에 카페가서 공부하고

오후에 인터넷 연걸이 되어서 집에서 편안하게 작업을 했다.

북마크보다 마이리스트라는 비슷한 기능이 우선적으로 진행되어야 한다해서

마이리스트를 새벽까지 코딩하여 구현해냈다.

 

튜터님의 조언도 듣고, 팀원들의 도움도 받으면서 배운점이 많았다.

 

@Entity()
export class CollectionItem {
  @PrimaryGeneratedColumn()
  id: number;

  @ManyToOne((type) => Collection, (collection) => collection.collectionItems)
  @JoinColumn({ name: 'collection_id' })
  collection: Collection;

  @ManyToOne((type) => Restaurant, (restaurant) => restaurant.collectionItems)
  @JoinColumn({ name: 'restaurant_id' })
  restaurant: Restaurant;

  @ManyToOne((type) => Post, (post) => post.collectionItems)
  @JoinColumn({ name: 'post_id' })
  post: Post;
}

컬렉션아이템이라는 엔티티에 콜렉션과 레스토랑 포스트를 연결하여 다대다 관계를 만들었다.

해당 정보를 꺼내써야 한다는게 매우 어려웠다. 이부분을 튜터님과 팀원들이 도와주었다.

 

우선 컨트롤러에만 dto를 사용할수 있다는걸 배웠고, (서비스에서도 사용할 수 있는지 헷갈렸다)

스웨거에 대해서 배웠다.

  // @UseGuards(AuthGuard('local'))
  @Get('/:userId')
  @ApiOkResponse({
    description: 'MyList 전체조회(해당 유저의 맛집리스트만 불러오기)',
  })
  @ApiUnauthorizedResponse({ description: '전체조회 실패' })
  async getMyLists(@Param('userId') userId: number) {
    const myLists = await this.myListService.getMyList(userId);
    return await myLists;
  }

위와 같이 작성하면 localhost:3000/api에서 자세한 명세를 볼 수 있어 프론트와의 협업이 편이했다.

swagger

https://bit.ly/3Jy1Sgg

 

Nestjs 프로젝트에 Swagger 도입하기

프로젝트의 사이즈가 커질수록 REST API의 수가 정말 어마 무시하게 많아지는데요, API가 문서화되지 않는다면 어떤 API들이 있었나 헷갈려서 소스코드 이곳저곳을 뒤적거리는 일이 발생할 수 있습

velog.io

위 주소를 참고하였고 쉽고 편리했다.

실제 데이터없고 프론트에서 넘겨줘야할 정보는 더미데이터를 만드는 법을 배웠다.

  @Post('/:collectionId')
  @ApiOkResponse({
    description: 'MyList 포스팅 추가',
  })
  @ApiUnauthorizedResponse({ description: 'MyList 포스팅 추가 실패' })
  async myListPlusPosting(@Param('collectionId') collectionId: number) {
    const postId = 1;    //<<=== 이부분을 배웠다.
    return this.myListService.myListPlusPosting(postId, collectionId);
  }
}

유저아이디를 넘겨줘야 하는데 프론트에서 할 수 있는 부분이거나 유즈가드를 사용해 할 수 있을거라 생각되어,

더미데이터를 넘겨 테스트 하였다.

 

실제 작동할때 필요한 로직을 담당하는 서비스에선 정말 많은 것을 배웠다.

  async getMyList(userId: number) {
    try {
      const myLists = await this.collectionRepository.find({
        where: { user_id: userId, deletedAt: null, type: 'myList' },
        select: { name: true, description: true, image: true },
      });

      return myLists;
    } catch (err) {
      console.error(err);
      throw new InternalServerErrorException(
        'Something went wrong while processing your request. Please try again later.',
      );
    }
  }

where, select의 사용법을 배웠고 앞으로 코딩할때 많은 도움이 될 것 같다.

  async createMyList(userId, name, type) {
    try {
      return this.collectionRepository.insert({
        user_id: userId,
        name,
        type: 'myList',
      });
    } catch (err) {
      console.error(err);
      throw new InternalServerErrorException(
        'Something went wrong while processing your request. Please try again later.',
      );
    }
  }

insert의 사용방법을 배웠다. user_id는 콜렉션 엔티티에 있는 데이터이고 해당 부분에 받아온 userId=1을 넘겼다.

 

async updateMyList(
    userId: number,
    collectionId: number,
    name: string,
    image: string,
    description: string,
    visibility: 'public' | 'private',
  ) {
    try {
      // id와 type이 모두 일치하는 Collection 엔티티를 찾는다.
      const myList = await this.collectionRepository.find({
        relations: {
          user: true,
        },
      });

      if (!myList) {
        throw new NotFoundException('마이리스트가 없습니다.');
      }

      await this.collectionRepository.update(
        { id: collectionId },
        {
          name,
          image,
          description,
          visibility,
        },
      );
    } catch (err) {
      if (err instanceof NotFoundException) {
        throw err;
      } else {
        console.error(err);
        throw new InternalServerErrorException(
          'Something went wrong while processing your request. Please try again later.',
        );
      }
    }
  }

이부분에선 다대다 관계에서 relations를 사용하여 해당 user데이터를 가져오는 방법을 배웠다.

추가적으로  console.error(err); 를 사용하여 상세한 에러를 보내라는 조언을 들었다.

 

async deleteMyList(id: number) {
    try {
      const result = await this.collectionRepository.softDelete(id); // soft delete를 시켜주는 것이 핵심입니다!
      if (result.affected === 0) {
        throw new NotFoundException('마이리스트가 없습니다.');
      }
    } catch (err) {
      if (err instanceof NotFoundException) {
        throw err;
      } else {
        console.error(err);
        throw new InternalServerErrorException(
          'Something went wrong while processing your request. Please try again later.',
        );
      }
    }
  }

위 부분에선 affected에 대해 배웠다. 해당 코드에 대한 설명의 GPT 답변을 첨부하였다.

영향을 받지 않았을때 0을 반환한다는 것이다.

마지막으로 가장 고생했던 마이리스트 포스팅 추가부분이다.

  async myListPlusPosting(postId: number, collectionId: number) {
    try {
      await this.collectionItemRepository.insert({
        post: { id: postId },
        collection: { id: collectionId },
      });
    } catch (err) {
      if (err instanceof NotFoundException) {
        throw err;
      } else {
        console.error(err);
        throw new InternalServerErrorException(
          'Something went wrong while processing your request. Please try again later.',
        );
      }
    }
  }
}

위 부분에서 고생한 점은 2가지다.

콜렉션아이디랑 포스트아이디를 넘겨줘야 하는 로직이다.

 

첫번째, 어떻게 포스팅정보를 넘겨줘야하지? 위 로직처럼 넘긴다면 너무 간단한거 아닌가?

 

이걸 넘겨받는 팀원이 한마디할 것 같았다. 그래서 아래로직처럼 생각했다.

관계를 맺어서 포스트와 유저의 실제정보를 가져와서 이러쿵 저렁쿵.....

튜터님의 조언을 받아 만든 코드였지만 실제로 넘길 데이터를 가져오는게 어려웠고

사실 정답은 다양하지만 넘겨받을 팀원과 얘기를 통해 insert로 기능을 구현했다.

 

너무 쉬운건 정답이 아닌거 아냐? 라는건 방탈출에서도 종종 겪었고 실제상황에서 자주 착각하는 것이다.

너무 어렵게 돌아가려고 했다. 알고리즘문제도 풀다보면 엄청쉽게 풀수도있고 어렵게 풀수도 있따.

const myLists = await this.collectionRepository.find({
  relations: {
    collectionItems: {
      post: true,
    },
    user: true,
  },
  where: {
    user: {
      id: userId,
    },
  },
});

 

두번쨰,  중간테이블(콜렉션아이템)에 어떻게 값을 넣을 수 있지?

      await this.collectionItemRepository.insert({
        post: { id: postId },
        collection: { id: collectionId },
      });
@Entity()
export class CollectionItem {
  @PrimaryGeneratedColumn()
  id: number;

  @ManyToOne((type) => Collection, (collection) => collection.collectionItems)
  @JoinColumn({ name: 'collection_id' })
  collection: Collection;

  @ManyToOne((type) => Restaurant, (restaurant) => restaurant.collectionItems)
  @JoinColumn({ name: 'restaurant_id' })
  restaurant: Restaurant;

  @ManyToOne((type) => Post, (post) => post.collectionItems)
  @JoinColumn({ name: 'post_id' })
  post: Post;
}

시행착오는 다음과 같다

postId: post, post:postId 등 작동원리를 모르니 별별 것을 다 시도해봤다.

그런데 팀원중 한분이 해결해주었다.

정확한 이해는 아니겠지만 이렇게 생각된다.

 

우선 엔티티에는 post: Post로 post에 Post라는 데이터 테이블이 담겼다고 생각한다.

그래서 왼쪽엔 post만 적었고, 오른쪽의 { id: postId }에서

id는 Post라는 테이블의 id값이고,

해당 아이디 값에 받아온 postId값을 넣는다.

 

정확한 개념일지 모르겠지만 이렇게 생각하니 좀 쉽게 느껴졌다.

결론적으로 정상작동 하였고 새벽2시에 모두 완료하니 오랜만에 기능을 완성하여 뿌듯함을 느꼈다.

728x90

북마크를 담당하게 되서 지금까지 못했던 기능구현을

시작하려고 했다. 회의도 있고 좀 늦어졌지만 이제야 프로젝트에 기여를 하는구나 생각하고 있는데

갑자기 집에 인터넷이 끊겼다. 공용전기세를 집주인이 안냈고, 누전 등의 문제로 한동안

사용을 못한다고 한다. 지금까지 한번 해보고 싶었던 카공(카페공부)를 해야하니

1년간 방치했던 노트북을 들고 카페로 갔다.

그런데 에디터인 비주얼 스튜디오도 안깔려있고, 데이터베이스를 연결해서 테스트해야하는

백앤드기능인데 데이터베이스를 연결하는게 오랜만이라 너무 어려웠다.

약3~4시간 걸려서 SQL SERVER, INSTALLER 등 깔고 연결했고,

겨우 몇시간 코딩을 할 수 있었다. 카페는 시끄럽고 불편했다...

스트레스는 많이 받았지만 그래도 새로운 경험이었다.

 

이날 배웠던 점은 깃허브 관리를 열심히 해야겠다란 점과

데이터베이스 연결하는 방법은 MYSQL의 인스톨러, 서버 등 자세히 알고 설치해야 한다는 점이었따.

728x90

소켓기능을 포기하고,

북마크 기능을 담당하게 되었다. 기본적인 CRUD이며, 꼭 필요한 기능이다.

 

기본적인 CRUD는 같은 조원의 코드를 참고하고,

데이터연결은 이전에 들었던 강의를 다시 들으며 복습하여 코드를 완성했다.

 

그런데 현재 ERD관계가 처음 작성했을때의 모습과 달라서

엔티티를 다시 수정중이다.

 

리더님이 작성해주신다하여 나는 TypeORM에 대해서 공부중이다.

https://typeorm.io/

 

TypeORM - Amazing ORM for TypeScript and JavaScript (ES7, ES6, ES5). Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server,

 

typeorm.io

공식홈페이지가 예시가 풍부하고 이해하기 쉽다.

해당 홈페이지를 참고해서 개념 정리해야겠다.

728x90

월요일, 화요일 2일간 코딩하면서 괴로웠다.

소켓의 기본 껍데기를 만드는건 쉬웠지만,

DB와 연동하여 데이터를 저장하고 불러오는 것이 어려웠다.

 

소켓에 대한 지식과 개념이 부족해서

전체적인 흐름을 잡기가 어려웠다.

 

결국 튜터님에게 상의받았으나

답변을 듣고도 진행이 어려울 것 같아서 팀원들에게

프로젝트에서 소켓 사용은 어려울 것 같다고 얘기했다.

 

첫번째 이유, 소켓을 사용하는 사람이 없어서 다른 동기 및 튜터님들의 도움 받기가 어렵다.

두번째 이유, 취업할때 소켓경험을 보는 곳이 얼마 없을 것이다. 작은 기업일수록 채팅은 개발하기 보단 API를 끌고 올듯.

세번쨰 이유, 나의 실력부족.

 

팀원들에게 미안하다고 얘기했고, 쪽지만들기로 가야되나 했다가

남아있는 기능 북마크로 가는건 어떠냐해서 북마크를 만들기로 했다.

기본적인 CRUD만드는 경험이 될 것 같고

모르더라도 물어보기가 소켓보다는 훨씬 쉬워질 것 같아서 마음이 편해졌다.

일주일 지나서 갑자기 포기하게 되었지만 더 늦어지면 차질이 생길 것 같았고

이 사실을 흔쾌히 받아준 팀원들에게 고마운 마음 뿐이다.

728x90

나에게 닥친 가장 큰 문제는

소켓관련 채팅 및 알람 ERD작성이 문제였다.

 

그리고 해당 문제에 대해 공부하던 도중

채팅관련 DB는 SQL보다 mongoose가 좋다는 사실을 알았고

강의와 블로그를 보며 데이터 연결을 시도했지만 에러가 계속 발생했다.

 

여러차례 시도하던중 데이터연결실패 10회이후,

상세한 에러문구를 발생한다는 것을 알았고 해당 문구를 보고 해결완료.

 

https://pyoja.tistory.com/208

 

[에러일기] 9회차 - 몽구스 데이터베이스 연결실패

1. 문제 [mongoosemodule] unable to connect to the database 강의와 문서들을 보며 몽구스를 연결시도했다. 그런데 서버실행만 하면 계속 위와 같은 문구가 발생하여 나를 괴롭혔다......ㅠㅠ 수차례 여러가지

pyoja.tistory.com

 

DB연결이 안될때가 정말 화가 나고 짜증난다.... 10번 기다려서 상세에러문구 받는걸 잊지 말아야지.

728x90

오늘도 오후4시쯤까지 팀원들과 와이어프레임,ERD에 대해 더 깊게 상의하고

튜터님 의견을 들으며 작성해갔다.

 

이후로는 소켓 기능구현하면서 문제가 생기는 부분을 수정해나갔다.

기존에 초기값을 주면서 접속하면 기본방으로 가는걸 없애고,

메시지 입력없이 전송하면 입력이 안되도록 설정하였다.

 

다음날은 소켓 ERD 작성과 데이터 입력하는 방법을 찾아낼 예정이다.

728x90

삼일절이자 공휴일인 최종프로젝트 3일차이다.

 

쉬는날인만큼 나가서 사람을 만날까 싶다가도

nestjs 소켓에 대한 걱정때문에 결국 집에서 공부하기로 했다.

 

덕분에 전혀 감이 안잡혔던 nestjs에서 소켓을 어떻게 사용해야하는지

조금은 감이 잡힌 것 같다.

 

많은 자료를 찾아봤지만

아래 링크에서 가장 많은 정보를 얻었다.

완성된 코드가 깃허브에 있어서 내려받아서 코드를 분석했다

https://blog.ewq.kr/37

 

NestJS + Websocket으로 채팅만들기 #1

최근 Websocket을 활용하여 사이드프로젝트를 진행한것이 있다. 추후에 오픈소스로 공개 할 예정이지만 kimpga 를 클론코딩하여 만든 https://hsct.io 이다. Websocket Client를 활용하여 서비스 구성하는것

blog.ewq.kr

 

728x90

오늘은 팀원 모두 ERD작성과 API 명세서를 작성했다.

ERD는 다 같이 소통하면서 정리하였고 이후에 튜터님이 와서 고쳐야 할 점을 얘기해주었다.

 

API명세서 같은 경우 내가 맡은 소켓부분은 일반 명세서와 달라서,

현재 소켓부분에 익숙하지 않기 때문에,

작업을 진행하면서 반대로 코드 작성후 명세서를 수정해나가야 할 것 같다.

 

튜터님의 서면피드백이 오기전까지

시간이 남아서 노드소켓을 복습했고 이후에

nest.js 소켓을 찾아 공부했는데 어려웠다.

 

노드소켓은 이해하고 뭔가 실습할 수 있을 것 같은데

nest소켓은 아직 기초조차 이해가 안가는 느낌..

 

외국인 영상과 공식문서 등 여러방면으로

공부할 방법을 찾아야 할 것 같다.

728x90

+ Recent posts