<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>park-sy13.log</title>
        <link>https://velog.io/</link>
        <description>배워나가는 중</description>
        <lastBuildDate>Thu, 25 Apr 2024 14:11:19 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>park-sy13.log</title>
            <url>https://velog.velcdn.com/images/park-sy13/profile/68619fcb-5384-4fd9-98b6-a8cd6d94fbf3/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. park-sy13.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/park-sy13" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[TIL 84일차 (20240425)]]></title>
            <link>https://velog.io/@park-sy13/TIL-84%EC%9D%BC%EC%B0%A8-20240425</link>
            <guid>https://velog.io/@park-sy13/TIL-84%EC%9D%BC%EC%B0%A8-20240425</guid>
            <pubDate>Thu, 25 Apr 2024 14:11:19 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/park-sy13/post/b53e8887-91cd-469e-b4a9-a84f216faaeb/image.png" alt="">
오늘 한 일</p>
<ul>
<li>aws s3에 이미지 업로드하기</li>
<li>유저 프로필 조회 시 이미지도 추가, 회원 가입 시 기본 이미지도 같이 등록하기</li>
<li>이미지 리사이징 파이프 추가 with sharp</li>
</ul>
<p>이미지 리사이징 파이프는 아래 블로그를 참고했다.
출처: <a href="https://velog.io/@devhslee02/NestJS-sharp%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%A6%AC%EC%82%AC%EC%9D%B4%EC%A7%95">https://velog.io/@devhslee02/NestJS-sharp%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%A6%AC%EC%82%AC%EC%9D%B4%EC%A7%95</a></p>
<hr>
<p>회원 가입 시 기본 이미지 할당은 먼저 s3에 저장되어있는 기본 이미지의 path를 먼저 file에 저장한 후, 해당 file을 유저에 저장하는 식으로 했다.</p>
<pre><code>const imageUrl = process.env.DEFAULT_PROFILE_IMAGE;

    const image = await this.fileRepository.findOneBy({ filePath: imageUrl });

const user = await this.userRepository.save({
      email: createUserDto.email,
      nickname: createUserDto.nickname,
      password: hashedPassword,
      file: image,
    });</code></pre><hr>
<p>아래는 프로필 이미지 변경, 기본 이미지로 변경하는 코드이다.</p>
<pre><code>/* 프로필 이미지 수정 */
  async addImage(user: User, file: Express.Multer.File) {
    const imagename = this.awsService.getUUID();

    const ext = file.originalname.split(&#39;.&#39;).pop();

    const fileName = `${imagename}.${ext}`;

    const imageUrl = `https://s3.${process.env.AWS_S3_REGION}.amazonaws.com/${process.env.AWS_S3_BUCKET_NAME}/${fileName}`;

    const userId = user.id;

    const newImageUrl = await this.awsService.imageUploadToS3(fileName, file, ext);

    const filePath = await this.fileRepository.save({ filePath: newImageUrl });
    await this.userRepository.update({ id: userId }, { file: filePath });

    return { message: &#39;프로필 이미지 수정 완료&#39; };
  }

// 기본 이미지로 변경

  async defaultImage(user: User) {
    const imageUrl = process.env.DEFAULT_PROFILE_IMAGE;

    const image = await this.fileRepository.findOneBy({ filePath: imageUrl });

    await this.userRepository.update({ id: user.id }, { file: image });

    return { message: &#39;기본 이미지로 변경 완료&#39; };
  }</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 83일차 (20240424)]]></title>
            <link>https://velog.io/@park-sy13/TIL-83%EC%9D%BC%EC%B0%A8-20240424</link>
            <guid>https://velog.io/@park-sy13/TIL-83%EC%9D%BC%EC%B0%A8-20240424</guid>
            <pubDate>Wed, 24 Apr 2024 12:43:53 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/park-sy13/post/491b75ca-e3ca-4666-b8fa-abe1ddbcbed1/image.png" alt="">
오늘 한 일</p>
<ul>
<li>db에서 dmRoomId에 해당하는 채팅 내역들 불러오기</li>
<li>채팅 버그 수정</li>
</ul>
<h3 id="1-채팅-버그-수정">1. 채팅 버그 수정</h3>
<p>오늘 일어난 채팅 에러는 한 dmroom에서 보낸 메세지가 다른 dmroom에 보내지는 에러였다. 시간을 많이 소요했던 이유는 이 에러가 처음부터 일어난게 아니라 한 5~6번은 정상적으로 특정 방에만 보내지다가 갑자기 다른 방에도 보내지던 에러라 이유를 발견하기 어려웠기 때문이다. 그래서 다른 코드를 참고 삼아 gateway를 많이 수정했다.
참고한 블로그 주소: <a href="https://kigo23.tistory.com/23">https://kigo23.tistory.com/23</a></p>
<pre><code>///event.gateway.ts

@WebSocketGateway({ namespace: ~~~~ })
export class DMGateway implements OnGatewayConnection, OnGatewayDisconnect {
  @WebSocketServer()
  server: Server;

  constructor(
    private readonly dmService: DMService,
    private readonly userService: UserService,
    private readonly jwtService: JwtService,
    private readonly config: ConfigService,
  ) {}

  connectedClients: { [socketId: string]: boolean } = {};
  clientNickname: { [socketId: string]: string } = {};
  roomUsers: { [key: string]: string[] } = {};

  async handleConnection(@ConnectedSocket() socket: Socket) {
    if (this.connectedClients[this.clientNickname.id]) {
      socket.disconnect(true);
      return;
    }

    this.connectedClients[socket.id] = true;

    const cookie = socket.handshake.headers.cookie;
    const user = await this.findUserByCookie(cookie);

    this.clientNickname[socket.id] = user.nickname;

    console.log(&#39;dm connected!&#39;);
  }

  async findUserByCookie(cookie: string) {
    // const cookie = socket.handshake.headers.cookie; 로 미리 받아와야함

    const token = cookie.split(&#39;=&#39;)[1];
    const payload = this.jwtService.verify(token, { secret: this.config.get&lt;string&gt;(&#39;JWT_SECRET_KEY&#39;) });
    const user = await this.userService.findUserByEmail(payload.email);

    return user;
  }

  async handleDisconnect(@ConnectedSocket() client: Socket) {
    delete this.connectedClients[client.id];
    const cookie = client.handshake.headers.cookie;
    const user = await this.findUserByCookie(cookie);

    Object.keys(this.roomUsers).forEach(room =&gt; {
      const index = this.roomUsers[room]?.indexOf(this.clientNickname[client.id]);

      if (index !== -1) {
        this.roomUsers[room].splice(index, 1);
      }
    });
  }

  @SubscribeMessage(&#39;sayBye&#39;)
  async leaveDMRoom(@ConnectedSocket() socket: Socket, @MessageBody() dmRoomId: string) {
    if (!socket.rooms.has(dmRoomId)) {
      return;
    }

    socket.leave(dmRoomId);

    const index = this.roomUsers[dmRoomId]?.indexOf(this.clientNickname[socket.id]);
    if (index !== -1) {
      this.roomUsers[dmRoomId].splice(index, 1);
      this.server.to(dmRoomId).emit(&#39;bye&#39;, { nickname: this.clientNickname[socket.id], dmRoomId });
    }

    const cookie = socket.handshake.headers.cookie;
    const user = await this.findUserByCookie(cookie);

    this.server.to(dmRoomId).emit(&#39;bye&#39;, { nickname: this.clientNickname[socket.id], dmRoomId });
  }

  @SubscribeMessage(&#39;joinDM&#39;)
  async handleJoinDM(@ConnectedSocket() socket: Socket, @MessageBody() dmRoomId: any) {
    if (socket.rooms.has(dmRoomId)) {
      return;
    }
    const cookie = socket.handshake.headers.cookie;
    const user = await this.findUserByCookie(cookie);

    socket.join(dmRoomId);
    console.log(`${user.nickname}의 벡엔드는 여기에 연결 중: ${dmRoomId}`);

    if (!this.roomUsers[dmRoomId]) {
      this.roomUsers[dmRoomId] = [];
    }

    this.roomUsers[dmRoomId].push(this.clientNickname[socket.id]);

    this.server.to(dmRoomId).emit(&#39;welcome&#39;, { nickname: this.clientNickname[socket.id], dmRoomId });
  }

  @SubscribeMessage(&#39;sendMessage&#39;)
  async handleMessage(@MessageBody() data, @ConnectedSocket() socket: Socket) {
    const cookie = socket.handshake.headers.cookie;
    const user = await this.findUserByCookie(cookie);

    const userId = user.id;
    const nickname = user.nickname;
    const content = data.value;
    const dmRoomId = +data.dmRoomId;

    await this.dmService.saveDM(dmRoomId, userId, content);

    socket.join(data.dmRoomId);
    console.log(&#39;socket 백엔드: &#39;, data.dmRoomId);

    const time = new Date();

    this.server.to(data.dmRoomId).emit(&#39;message&#39;, { dmRoomId, nickname, content, time });
  }

  @SubscribeMessage(&#39;dmRoomList&#39;)
  async dmRoomList(socket: Socket) {
    const cookie = socket.handshake.headers.cookie;
    const user = await this.findUserByCookie(cookie);
    const userId = user.id;

    const dmRooms = await this.dmService.getDMRooms(user.id);

    const promiseDmRoomIds = dmRooms.map(async e =&gt; {
      const id = e.dmRoom_id;
      const nickname1 = e.fr_nickname;
      const nickname2 = e.us_nickname;
      const userNickname = user.nickname;

      if (userNickname === nickname1) {
        return { id, nickname: nickname2 };
      } else if (userNickname === nickname2) {
        return { id, nickname: nickname1 };
      }
    });

    const dmRoomIds = await Promise.all(promiseDmRoomIds);

    socket.emit(&#39;rooms&#39;, dmRoomIds);
  }
}</code></pre><p>nickname과 socket의 이름을 따로 받았다. 그리고 data가 disconnect될 때 socket.leave(dmRoomId);을 추가했는데 아마 이 부분이 없었기에 지난번에 그 에러가 나지 않았을까 싶다.</p>
<hr>
<h3 id="이전-채팅-내역-불러오기">이전 채팅 내역 불러오기</h3>
<p>원래 계획은 gateway에서 dmService를 불러와 가져오는 것이었다. 그런데 튜터님의 조언으로 프론트에서 api를 호출하여 불러오는 것으로 변경했다. 다행히 어제 fetch를 많이 작성해서 오래 걸리지는 않았다. fetch만 ㅋ... 이걸 화면에 보내주고 새 채팅까지 합치는 것이 더 많이 걸렸다.</p>
<pre><code>// api 호출하고 출력까지 하는 프론트 함수

fetch(`http://localhost:3000/dm/history/${dmRoomId}`,{
  headers:{
    Authorization:`Bearer ${token}`
}
})
.then(res=&gt;res.json())
.then(data=&gt;data.map(chat=&gt;{
  sendDM(`${chat.us_nickname}:${chat.chat_content} ${chat.chat_created_at}`)
}))
.catch(err=&gt; console.error(&#39;채팅 내역 가져오는데 오류 발생: &#39;, err));
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 82일차 (20240423)]]></title>
            <link>https://velog.io/@park-sy13/TIL-82%EC%9D%BC%EC%B0%A8-20240423</link>
            <guid>https://velog.io/@park-sy13/TIL-82%EC%9D%BC%EC%B0%A8-20240423</guid>
            <pubDate>Tue, 23 Apr 2024 04:40:07 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/park-sy13/post/17855c3f-1f21-4f1d-be88-285cd4824c47/image.png" alt="">
오늘의 목표</p>
<ul>
<li>로그아웃, 회원가입 메인 페이지에 연결, 프론트 구현</li>
<li>채팅에서 퇴장 메세지까지 삭제</li>
<li>채팅과 DB 연결해서 방 열릴 때 내역들 같이 뜨도록</li>
<li>access token 만료 시 refresh token으로 재발급 받기 (제발 좀 빨리 해라 나야...)</li>
<li>시간 된다면 프로필 이미지 저장까지...</li>
</ul>
<h3 id="1-로그아웃-구현">1. 로그아웃 구현</h3>
<p>와이어프레임에서 로그아웃 페이지는 따로 없이 버튼으로만 작동하게끔 기획했기 때문에 js만 새로 만들었다.</p>
<pre><code>// logout.js

const token = window.localStorage.getItem(&#39;authorization&#39;);

function logout(){
    fetch(&#39;http://localhost:3000/user/logout&#39;,{
        method:&#39;POST&#39;,
        headers:{
            Authorization:`Bearer ${token}`
        }
    })
    .then(res=&gt;{
        if(res.status === 201){
            window.localStorage.removeItem(&#39;authorization&#39;);

            alert(&#39;로그아웃 성공&#39;);

            window.location.href = &#39;http://localhost:3000/main&#39;;
        }else{
            alert(&#39;로그아웃 실패&#39;);
        }
    })
    .catch(error =&gt; 
        console.error(&#39;Error:&#39;, error));
}

document.getElementById(&#39;logout&#39;).addEventListener(&#39;click&#39;, logout)</code></pre><p>이 때 컨트롤러에서 passport의 UseGuards를 쓰므로 headers에 Authorization과 Bearer을 첨가해서 보내준다.</p>
<p>참고: <a href="https://reqbin.com/code/javascript/ricgaie0/javascript-fetch-bearer-token">https://reqbin.com/code/javascript/ricgaie0/javascript-fetch-bearer-token</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 81일차(20240422)]]></title>
            <link>https://velog.io/@park-sy13/TIL-81%EC%9D%BC%EC%B0%A820240422</link>
            <guid>https://velog.io/@park-sy13/TIL-81%EC%9D%BC%EC%B0%A820240422</guid>
            <pubDate>Mon, 22 Apr 2024 11:27:53 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/park-sy13/post/c1f88c12-853a-4a1d-8d93-5e7b3f268fc4/image.png" alt="">
오늘 면접 카타 때는 데드록(교착 상태)에 대해서 학습했다. 튜터님들의 조언(운영 체제에서 많이 배우는 개념, 데드락 사전 방지와 해결 방법, 스탑에이션 추가 공부)을 들을 수 있어서 좋았으나 내 허약한 목에는 좋지 않은 시간이었다... (팀원이 감기걸렸냐는 걱정까지 함 ㅋ)</p>
<h2 id="🥔🥔🥔-오늘-마주한-에러들">🥔🥔🥔 오늘 마주한 에러들</h2>
<h3 id="1-req-res-둘다-같이-작성하면-생기는-에러">1. req, res 둘다 같이 작성하면 생기는 에러</h3>
<p>원래 목표는 프론트 쪽 콘솔에서 쿠키를 받아오고, access token 쿠키 존재 여부를 따져 회원가입 로그인 버튼 / 채팅, dm 버튼이 뜨도록 하려고 했다. 하지만 document.cookie를 콘솔 로그로 찍어보니 빈칸만 나왔고, 알고보니 브라우저에서는 쿠키를 보안 정책으로 인해 조회, 가공할 수 없다는 것이었다. (httpOnly 문제인 줄 알았더니 그냥 브라우저에서 막은 것이었다. 어쩐지 주말동안 용을 써도 안되더라...) 결국 서버에서 쿠키를 조회하고 있으면(로그인 한 유저) true, 없으면 false를 반환하려 했더니...</p>
<p><img src="https://velog.velcdn.com/images/park-sy13/post/0f45f258-cdf0-49d3-bf70-5691be15e9f1/image.png" alt=""></p>
<p>controller에서 req.cookie를 받아오고 res.status(200).json(어쩌고저쩌고)를 작성했더니 발생했다. 구글링해보니 res를 return으로 주라 해서 주었더니</p>
<pre><code>converting circular structure to json --&gt; starting at object with constructor &#39;socket&#39; | property &#39;parser&#39; -&gt; object with constructor &#39;httpparser&#39; --- property &#39;socket&#39; closes the circle</code></pre><p>에러가 발생했다. 환장했다. 하필 하나의 js가 아닌 로그인과 메인 페이지를 오가는 상황이라 더욱 복잡했다.</p>
<p>이 방법을 구글링해보면 console.log()와 for문을 사용하라 하지만 나는 백엔드에서 보낸 true와 false만 필요한 사람이다. promise 객체로 받아오는 것도 지겹다 (fetch를 이용해서 어쩔 수 없이 비동기가 받아진다.) 아무튼 4시간 넘게 실랑이 한 결과 해결하였는데 아래의 코드는 해결된 코드이다.</p>
<pre><code>// mainPage.js

// 실행되자마자 로그인 했는지 안했는지 판별하는 함수 실행
window.onload = function() {
        checkLoginStatus();
    };

    function checkLoginStatus(){
    fetch(&#39;http://localhost:3000/user/checkLogin&#39;,{
        method:&#39;GET&#39;,
    })
    .then(res =&gt;{return res.json()})
    .then((json)=&gt;{
        if(json.isLoggedIn){
            showLoggedInUI()
        }else{
            showLoggedOutUI()
        }
    }
    ).catch(error=&gt;{
        console.error(&#39;mainPage checkLogin에서 일어난 에러: &#39;, error);
    })
    }</code></pre><pre><code>//user.controller.ts

// 로그인했는지 안했는지 확인하기
  @Get(&#39;checkLogin&#39;)
  async checkLogin(@Req() req: Request) {
    const check = await this.userService.checkLogin(req.cookies);

    if (check) {
      return { isLoggedIn: true };
    } else {
      return { isLoggedIn: false };
    }
  }</code></pre><pre><code>//user.service.ts

async checkLogin(cookies) {
    const key = Object.keys(cookies);

    if (key[0] === &#39;authorization&#39;) {
      return true;
    } else {
      return false;
    }
  }</code></pre><p>사실 서비스 부분에서 키의 맨 처음 값을 받아버렸는데 이는 지금 쿠키에 넣은 값이 jwt 쿠키밖에 없기 때문이다... 만약 다른 정보도 쿠키에 넣는다면 service의 수정이 필요할 것이다.</p>
<p>우선 해결 방법으로는 res.status().json()으로 보내지 말고 바로 return {}을 사용해서 내보내는 방법이었다. json을 사용하니 Socket에서 http에서 다시 socket으로 어쩌고 저쩌고 에러들이 쫘락 나와서 그냥 배열로 전해줬다. 다행히 잘 받아오고 잘 실행되었다!</p>
<h4 id="⭐-오늘-얻은-교훈-브라우저-창에서-쿠키-다루지-말자-⭐">⭐ 오늘 얻은 교훈: 브라우저 창에서 쿠키 다루지 말자 ⭐</h4>
<hr>
<h3 id="2-채팅방을-옮길-때-이전-방에서-남겼던-채팅-기록들이-유지됨">2. 채팅방을 옮길 때 이전 방에서 남겼던 채팅 기록들이 유지됨</h3>
<p>우리 멍충한 자바스크립트... 눈치 빠르게 행동하지 못하고 이전에 남겼던 것들은 새로고침 하기 전까지 남겨줄 거란 의지를 가지고 있다. 결국 직접 삭제하라는 코드 명령을 내려줘야 한다.</p>
<p>우선 hbs 파일은 이렇다.
<img src="https://velog.velcdn.com/images/park-sy13/post/4b2af253-df7e-4259-acb5-b3d8e05e1dcc/image.png" alt="">
여기서 ul id=&#39;newChats&#39;에 li들이 생길건데 이 li들을 모조리 지워야하는 것.</p>
<p>문제는 이들이 node이기 때문에 getElemetsByTagName으로 가져오면 지울 수 없다는 문제가 발생한다.
 liElementes.forEach is not a function
분명 타입이 object인걸 찍었거늘 forEach와 map 모두 안된다고 한다.
우선 해결한 코드는 다음과 같다.</p>
<pre><code>const newChats = document.getElementById(&#39;newChats&#39;);
  const pastChats = newChats.querySelectorAll(&#39;li&#39;);
  const chatsArray = Array.from(pastChats);
  chatsArray.forEach((li)=&gt;{
    li.remove();
  })</code></pre><p>querySelectorAll로 받아오고 배열로 만들어버리기... 정말 프론트의 세계는 알다가도 모르겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 80일차 (20240419)]]></title>
            <link>https://velog.io/@park-sy13/TIL-80%EC%9D%BC%EC%B0%A8-20240419</link>
            <guid>https://velog.io/@park-sy13/TIL-80%EC%9D%BC%EC%B0%A8-20240419</guid>
            <pubDate>Fri, 19 Apr 2024 11:22:02 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/park-sy13/post/a13f5cb1-c8a2-4778-aae3-8af2c040f064/image.png" alt="">
어제 썼어야했는데... 이제야 쓰는 TIL</p>
<p>어제와 오늘 한 일</p>
<ul>
<li>socket에 to 보내는 것 안되던 에러 해결</li>
<li>main.html, dmIndex.html 모두 hbs로 변환</li>
<li>login을 axios 사용해서 get 요청을 먼저 보내기</li>
</ul>
<p>이제 해야하는 일</p>
<ul>
<li>jwt 토큰 만료 시 accessToken 재발급</li>
<li>채팅 broadcasting</li>
<li>방 바꾸면 기존 메세지 내역들 삭제</li>
<li>방 입장하면 이전 채팅들 뜨도록 바로 조회하기</li>
<li>친구 조회 시 친구 추천 기능 뜨도록</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 78일차 (20240418)]]></title>
            <link>https://velog.io/@park-sy13/TIL-78%EC%9D%BC%EC%B0%A8-20240418</link>
            <guid>https://velog.io/@park-sy13/TIL-78%EC%9D%BC%EC%B0%A8-20240418</guid>
            <pubDate>Thu, 18 Apr 2024 00:35:20 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/park-sy13/post/aaf61178-ae15-49c9-a35e-0d6d9dc1c47b/image.png" alt="">
오늘은 해결한 문제보다 오히려 미궁 속에 더 빠져버렸다... 원인은? socket.io ㅂㄷㅂㄷ... 다른 사람들은 잘만 하는 것을 나만 애먹고 있어서 처량하다...</p>
<p>아직 남아있는 문제</p>
<ul>
<li>처음 입장할 때 유저 정보 받아서 디엠방들 나열하기 (이 때 디엠방 옆에 입장하기 버튼 눌러서 입장시킬 것)</li>
<li>디엠방 생성과 입장 나누기</li>
<li>다른 유저가 로그인하면 기존 유저가 퇴장하는 현상 수정하기 (이 부분은 아마 쿠키가 추가되는 것이 아닌 수정되는 문제때문인 것같다.
아마 쿠키 설정 문제인 것같은데 겸사겸사 refreshToken으로 만료되면 accessToken 재발급하는 것을 추가해야겠다. 이 부분을 어디서 담당해야하는지 몰라서 미뤄두고 있었는데 이 곳에서 터지네 ^ㅠ^...)</li>
<li>특정 방에만 디엠을 보내야하는데 전체적으로 까발려진다;;; 이 부분도 수정해야지...</li>
<li>main 홈페이지에서 로그인을 하면 회원가입/로그인 버튼 대신 채팅/dm 버튼으로 수정되어야하는데 기존 것이 유지되므로 이 부분도 수정...</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 77일차 (20240416)]]></title>
            <link>https://velog.io/@park-sy13/TIL-77%EC%9D%BC%EC%B0%A8-20240416</link>
            <guid>https://velog.io/@park-sy13/TIL-77%EC%9D%BC%EC%B0%A8-20240416</guid>
            <pubDate>Tue, 16 Apr 2024 12:20:18 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/park-sy13/post/fb8ad29f-ded5-4fc7-a8a8-23d3147c27ba/image.png" alt="">
오늘은 중간발표. 다만 발표 자료가 미흡했는지 예상한 기술 쪽 피드백이 아닌 주제와 기능 쪽 피드백만 받았다. 그 부분이 몹시 아쉬웠고, 다른 조들의 발표를 보면서 겹치는 기능이 너무 많아 특색이 없다는 생각이 강하게 들었다. 이 부분은 팀원들과 상의해서 새 기능을 추가하든 해야할 것같다. 그리고 도전적인 기술을 하라고 하시는데 nestjs로 할 수 있는 어떤 기술을 바라시는건지 이해가 사실 안간다... 다 거기서 거기라는 게으른 생각이 이미 파고 든 것같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 76일차 (20240415)]]></title>
            <link>https://velog.io/@park-sy13/TIL-76%EC%9D%BC%EC%B0%A8-20240415</link>
            <guid>https://velog.io/@park-sy13/TIL-76%EC%9D%BC%EC%B0%A8-20240415</guid>
            <pubDate>Mon, 15 Apr 2024 14:12:22 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/park-sy13/post/8d506bc1-90d8-4cd3-9b70-7da6c7aed392/image.png" alt="">
오늘 한 일</p>
<ul>
<li>dmModule 의존성 설정</li>
<li>postman으로 socket test (진행중)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/park-sy13/post/8e19ff48-8281-43c3-91ec-2bc7dedf549a/image.png" alt="">
오늘 마주한 에러인 의존성 문제... 모듈을 생성할 때마다 마주해서 처음 실행이 놀랍지 않다 ㅎ ㅠㅠ...</p>
<p>이번 모듈은 dmModule인데 userservice와 dmservice를 불러와야하는 상황. jwtmodule도 불러오고 서비스도 불러왔는데 여전히 에러가 떠서 무엇이 문제인가 했더니 dmservice에서 export를 안해줘서 받아오지 못하는 상황이었다...
<img src="https://velog.velcdn.com/images/park-sy13/post/7ca4a6fb-696d-4231-bdfe-f47792c0331f/image.png" alt="">
결국 추가해주고 나서 서버 구동이 되었다! (이 문제로 2시간 소요..)</p>
<hr>
<p>다음 문제는 포스트맨 사용 문제. 처음 사용해보는 툴이라서 많이 서툴었지만 socket을 테스트할 수 있는 곳이기에 이번에 처음 시도해보았다. 처음 마주한 문제는 socket 연결을 아예 못하는 문제. 이 문제로 1시간 정도 끙끙대었으나 해결법은 인터넷 브라우저에서 실행할 것이 아니라 포스트맨을 다운받아 사용하면 되는 문제였다...
다음 문제는 테스트를 어떻게 하느냐의 문제였다. 기존의 http 메서드들을 이용한 테스트는 get post put 등 다 명시되어 있었는데 socket io는 아니었다...<img src="https://velog.velcdn.com/images/park-sy13/post/5155b86a-78b1-4394-9741-08f994f3b006/image.png" alt="">
유튜브를 보니 하단에 추가로 작성하던데 문제는 하단에 작성하니 에러가 나는 것... 이 에러는 포스트맨 쪽보다는 내 gateway와 프론트 연결 문제인 것같아서 다시 코드를 싸매야할 것같다...
중간 발표 전 채팅 구현하겠다는 큰 포부는 하늘 위로...</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 75일차 (20240412)]]></title>
            <link>https://velog.io/@park-sy13/TIL-75%EC%9D%BC%EC%B0%A8-20240412</link>
            <guid>https://velog.io/@park-sy13/TIL-75%EC%9D%BC%EC%B0%A8-20240412</guid>
            <pubDate>Mon, 15 Apr 2024 02:50:13 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/park-sy13/post/50841bbe-4fad-45a6-89c5-7d60bfc69f45/image.png" alt="">
오늘 한 일</p>
<ul>
<li>dmIndex.html 작성</li>
<li>express의 response 못 가져오는 것 수정</li>
</ul>
<pre><code>&lt;body&gt;
    &lt;header&gt;&lt;h1&gt;FriendDM&lt;/h1&gt;&lt;/header&gt;
    &lt;main&gt;
      &lt;div id=&quot;dmMain&quot;&gt;
        &lt;form&gt;
          &lt;input placeholder=&quot;dmRoomId&quot; , type=&quot;number&quot; /&gt;
          &lt;button&gt;DM Room 입장&lt;/button&gt;
        &lt;/form&gt;
        &lt;h4&gt;DM Rooms:&lt;/h4&gt;
        &lt;ul&gt;&lt;/ul&gt;
      &lt;/div&gt;

      &lt;div id=&quot;dmRoom&quot;&gt;
        &lt;h3&gt;&lt;/h3&gt;
        &lt;ul&gt;&lt;/ul&gt;
        &lt;form id=&quot;message&quot;&gt;
          &lt;input placeholder=&quot;메세지를 입력하세요.&quot; , type=&quot;text&quot; /&gt;
          &lt;button&gt;전송&lt;/button&gt;
        &lt;/form&gt;
      &lt;/div&gt;
    &lt;/main&gt;
  &lt;/body&gt;
  &lt;script src=&quot;http://localhost:3000/socket.io/socket.io.js&quot;&gt;&lt;/script&gt;
  &lt;script&gt;
    const socket = io(&#39;http://localhost:3000/direct-message&#39;, {
      reconnection: true,
      reconnectionDelay: 1000,
      reconnectionDelayMax: 5000,
      reconnectionAttempts: Infinity,
    });
    const nickname = prompt(&#39;닉네임&#39;);
    const dmMain = document.getElementById(&#39;dmMain&#39;);
    const form = dmMain.querySelector(&#39;form&#39;);
    const dmRoom = document.getElementById(&#39;dmRoom&#39;);

    dmRoom.hidden = true;

    let dmRoomId = &#39;&#39;;

    function sendDM(message) {
      const ul = dmRoom.querySelector(&#39;ul&#39;);
      const li = document.createElement(&#39;li&#39;);
      li.innerText = message;
      ul.appendChild(li);
    }

    function handleDMSubmit(event) {
      event.preventDefault();
      const input = dmRoom.querySelector(&#39;#message input&#39;);
      const value = input.value;
      socket.emit(&#39;new_message&#39;, value, dmRoomId, () =&gt; {
        sendDM(`${nickname}:${value}`);
      });
      input.value = &#39;&#39;;
    }

    function createDMRoom() {
      dmMain.hidden = true;
      dmRoom.hidden = false;
      const h3 = dmRoom.querySelector(&#39;h3&#39;);
      h3.innerText = `DMRoom ${dmRoomId}`;

      const messageForm = dmRoom.querySelector(&#39;#message&#39;);
      messageForm.addEventListener(&#39;submit&#39;, handleDMSubmit);
    }

    function handleDMRoomSubmit(event) {
      event.preventDefault();
      const input = form.querySelector(&#39;input&#39;);
      socket.emit(&#39;enter_DMroom&#39;, input.value, createDMRoom);
      dmRoomId = input.value;
      input.value = &#39;&#39;;
    }

    form.addEventListener(&#39;submit&#39;, handleDMRoomSubmit);

    socket.on(&#39;welcome&#39;, user =&gt; {
      const h3 = dmRoom.querySelector(&#39;h3&#39;);
      h3.innerText = `DMRoom ${dmRoomId}`;
      sendDM(`${user}이 입장했습니다.`);
    });

    socket.on(&#39;bye&#39;, user =&gt; {
      const h3 = dmRoom.querySelector(&#39;h3&#39;);
      h3.innerText = `DMRoom ${dmRoomId}`;
      sendDM(`${user}이 퇴장했습니다.`);
    });

    socket.on(&#39;new_message&#39;, sendDM);

    socket.on(&#39;DMRoom_change&#39;, dmRooms =&gt; {
      const dmRoomList = dmMain.querySelector(&#39;ul&#39;);
      dmRoomList.innerHTML = &#39;&#39;;
      if (dmRooms.length === 0) {
        dmRoomList.innerHTML = &#39;&#39;;
        return;
      }

      dmRooms.forEach(room =&gt; {
        const li = document.createElement(&#39;li&#39;);
        li.innerText = room;
        dmRoomList.append(li);
      });
    });
  &lt;/script&gt;
&lt;/html&gt;</code></pre><p>참고자료: <a href="https://nomadcoders.co/noom/lectures/3089s">https://nomadcoders.co/noom/lectures/3089s</a></p>
<hr>
<p>오늘 마주한 에러
<img src="https://velog.velcdn.com/images/park-sy13/post/235578b0-94fd-4446-8941-ecc493598d4f/image.png" alt="">
express 를 못 불러오는 에러였다. 해결방법은 node_modules와 dist, package-lock.json을 지우고 npm i로 다시 받아오는 것 + 겸사겸사 쓰지 않는 패키지들 삭제</p>
<p>아마 이전에 git pull 받으면서 충돌났던 package-lock.json을 건드린게 문제가 된 것같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 74일차 (20240411)]]></title>
            <link>https://velog.io/@park-sy13/TIL-74%EC%9D%BC%EC%B0%A8-20240411</link>
            <guid>https://velog.io/@park-sy13/TIL-74%EC%9D%BC%EC%B0%A8-20240411</guid>
            <pubDate>Fri, 12 Apr 2024 00:14:35 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/park-sy13/post/9e483463-01b8-445b-90da-1cc330b8c865/image.png" alt="">
오늘 한 일</p>
<ul>
<li>튜터님과 모의면접 예상 질문 준비하기</li>
<li>socket.io 강의 수강</li>
</ul>
<p>현재 socket.io를 듣고 있는데 문제는 javascript와 express, 아니면 spring이나 react를 병행하는 자료들이 많고 nestjs를 사용하는 자료가 적어서... 어떻게 nestjs에 사용할 수 있을지 고민하고 서치하는데 시간이 너무 많이 소요되었다. 원래 사용하려던 기술은 webRTC인데 다른 팀원을 보니 socket.io와 websocket을 사용해 텍스트 채팅을 구현한 것을 보고 중간발표를 위해 똑같이 websocket으로 준비해야할 지 고민이 많이 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 73일차 (202040409)]]></title>
            <link>https://velog.io/@park-sy13/TIL-73%EC%9D%BC%EC%B0%A8-202040409</link>
            <guid>https://velog.io/@park-sy13/TIL-73%EC%9D%BC%EC%B0%A8-202040409</guid>
            <pubDate>Wed, 10 Apr 2024 15:17:23 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/park-sy13/post/02a1e975-2efd-4b3a-9777-6199c06852eb/image.png" alt="">
오늘 한 일</p>
<ul>
<li>webRTC에 대해서 알아보기</li>
</ul>
<p>구글링하면 이해하기 어려운 코드들만 마주해서 유튜브로 돌려서 서치하다가 하나씩 풀어주는 무료 강의가 있길래 수강 중... (현재 진행형)
지금은 websocket으로 실시간 1:1로 채팅하는 것까지 따라했다.
<img src="https://velog.velcdn.com/images/park-sy13/post/9bd5aa15-737b-4eb6-a9ea-d4f6197bf98e/image.png" alt="">
(출처: <a href="https://nomadcoders.co/noom/lectures/3099">https://nomadcoders.co/noom/lectures/3099</a>)</p>
<p>오랜만에 프론트엔드 코드들을 보니 또 막막해지는 것... 😭</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 72일차 (20240408)]]></title>
            <link>https://velog.io/@park-sy13/TIL-72%EC%9D%BC%EC%B0%A8-20240408</link>
            <guid>https://velog.io/@park-sy13/TIL-72%EC%9D%BC%EC%B0%A8-20240408</guid>
            <pubDate>Mon, 08 Apr 2024 13:57:07 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/park-sy13/post/bd9c95f5-169d-4a79-bf4c-57ba1d7b7261/image.png" alt="">
오늘 한 일</p>
<ul>
<li>createQueryBuilder을 사용해서 친구 관련 기능(친구 요청, 친구창 조회, 내가 보낸 친구 요청 조회, 나에게 온 친구 요청 조회, 친구 수락, 친구 삭제) 구현하기!</li>
</ul>
<h3 id="✏️-조회하기-기능">✏️ 조회하기 기능</h3>
<pre><code>return await this.friendshipRepository
      .createQueryBuilder(&#39;friendship&#39;)
      .select([&#39;friendship.id&#39;, &#39;friendship.is_friend&#39;, &#39;us.id&#39;, &#39;us.nickname&#39;, &#39;fr.id&#39;, &#39;fr.nickname&#39;])
      .where(&#39;friendship.user_id = :user_id&#39;, { user_id: user.id })
      .orWhere(&#39;friendship.friend_id = :friend_id&#39;, { friend_id: user.id })
      .andWhere(&#39;friendship.is_friend = true&#39;)
      .leftJoin(&#39;friendship.user&#39;, &#39;us&#39;)
      .leftJoin(&#39;friendship.friend&#39;, &#39;fr&#39;)
      .getRawMany();</code></pre><p>  leftjoin을 쓰면 마지막 출력시 getMany()가 아닌 getRawMany()로 해야 제대로 출력이 된다!
(출처: <a href="https://stackoverflow.com/questions/64401212/how-to-select-specific-columns-in-typeorm-querybuilder">https://stackoverflow.com/questions/64401212/how-to-select-specific-columns-in-typeorm-querybuilder</a>)</p>
<hr>
<h3 id="✏️-수정하기-기능">✏️ 수정하기 기능</h3>
<pre><code>await this.friendshipRepository
      .createQueryBuilder()
      .update(Friendship)
      .set({ is_friend: true })
      .where(&#39;friend_id = :friend_id&#39;, { friend_id: user.id })
      .andWhere(&#39;id = :id&#39;, { id })
      .execute();</code></pre><p>update의 경우 update와 set을 사용하고 execute()로 마무리한다.</p>
<hr>
<ul>
<li>오늘도 애먹은 부분은 바로... module에서 관계 나타내기...<pre><code>@Module({
imports: [
  JwtModule.registerAsync({
    useFactory: (config: ConfigService) =&gt; ({
      secret: config.get&lt;string&gt;(&#39;JWT_SECRET_KEY&#39;),
    }),
    inject: [ConfigService],
  }),
  TypeOrmModule.forFeature([User, Friendship]),
  UserModule,
],
controllers: [FriendController],
providers: [FriendService],
})</code></pre>imports 안에는 사용할 엔티티와 모듈을, controller와 providers에는 불러올 controller와 service를 적는데 항상 헷갈린다. (그리고 항상 똑같은 에러를 마주하고 시간을 허비한다...)</li>
</ul>
<hr>
<p>내일 해야할 일</p>
<ul>
<li>디엠 방 생성하기</li>
<li>socket.io이용해서 채팅 기능 구현하기</li>
<li>디엠 쪽 모듈 새로 생성하기</li>
<li>access token 만료 시 refresh token 생성할 구조 만들기</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 71일차 (20240405)]]></title>
            <link>https://velog.io/@park-sy13/TIL-71%EC%9D%BC%EC%B0%A8-20240405</link>
            <guid>https://velog.io/@park-sy13/TIL-71%EC%9D%BC%EC%B0%A8-20240405</guid>
            <pubDate>Fri, 05 Apr 2024 12:37:50 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/park-sy13/post/03d75b84-7387-4723-9ec0-f5df8099ac26/image.png" alt="">
오늘 한 일</p>
<ul>
<li>레디스 패키지 다시 바꿔서 적용시키기</li>
<li>로그아웃 기능 만들기</li>
<li>회원 탈퇴 기능 만들기</li>
<li>친구 폴더 새로 생성, 기본 틀 만들기</li>
</ul>
<p>다음 주에 해야 할 일</p>
<ul>
<li>친구 추가, 관리 기능</li>
<li>채팅 기능 (텍스트, 음성, 화상) 구현 (내 컴퓨터가 버틸 수 있을까...?)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 70일차 (20240404)]]></title>
            <link>https://velog.io/@park-sy13/TIL-70%EC%9D%BC%EC%B0%A8-20240404</link>
            <guid>https://velog.io/@park-sy13/TIL-70%EC%9D%BC%EC%B0%A8-20240404</guid>
            <pubDate>Fri, 05 Apr 2024 00:14:48 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/park-sy13/post/5c580956-75be-466c-8101-410cf317b70a/image.png" alt="">
오늘 한 일</p>
<ul>
<li>유저 게임 관심 장르 수정하기</li>
</ul>
<p>과정을 나누자면</p>
<h4 id="컨트롤러">컨트롤러</h4>
<ol>
<li>입력받은 장르가 빈칸이면 모두 삭제인 것으로 간주, &#39;관심 장르를 모두 삭제하는 service의 함수&#39;로 보냄</li>
<li>입력받은 장르가 1개 이상이면 수정으로 간주, &#39;겹치지 않은 기존의 관심 장르를 삭제하는 함수&#39;로 보내고 새로 추가해야할 장르의 배열을 service로부터 받음</li>
<li>2의 함수의 결과물을 &#39;새로운 관심 장르를 생성하는 함수&#39;로 보냄</li>
</ol>
<h4 id="겹치지-않은-기존의-관심-장르를-삭제하는-함수">겹치지 않은 기존의 관심 장르를 삭제하는 함수</h4>
<ol>
<li>유저 정보로 있는 관심 장르들 모두 soft delete된 것들을 모두 복구 (이때 가입 시 아무것도 설정하지 않았다면 바로 컨트롤러 3의 함수로 내보냄)</li>
<li>회원의 관심 장르 데이터들의 장르 아이디와 입력한 새 장르 아이디를 비교, 같으면 유지, 새 장르 아이디와 다르면 soft delete 진행</li>
<li>위에서 유지한 장르 아이디를 새 장르 아이디에서 제외시키고 최종 새 장르 아이디들 배열을 return 시킴
<img src="https://velog.velcdn.com/images/park-sy13/post/44542a77-4019-4f30-806f-9dedb0dc8773/image.png" alt=""></li>
</ol>
<h4 id="새로운-관심-장르-생성-함수">새로운 관심 장르 생성 함수</h4>
<p>위의 함수에서 유저의 아이디와 장르 아이디를 배열로 받은 후 map으로 하나씩 interestGenre를 생성함
<img src="https://velog.velcdn.com/images/park-sy13/post/1b07265e-daa0-40c4-949b-789f999c365c/image.png" alt=""></p>
<hr>
<p>interestGenre의 구조를 보면</p>
<ul>
<li>id (interestGenre의 아이디)</li>
<li>deleteAt</li>
<li>user_id (외래키)</li>
<li>genre_id (외래키)
인지라 외래키인 유저 정보로 interestGenre를 조회하기 위해서는 orm이 아니라 createQueryBuilder()의 도움을 받아야했다.</li>
</ul>
<p>위에서 보면 .restore()은 softdelete된 정보들을 복구하는 메서드이고, leftJoinAndSelect()는 외래키 정보까지 가져오는 메서드이며, where절을 쓸 때는 &#39;ig.user_id = :user_id&#39;, { user_id: id } (ig는 alias이므로 신경쓰지 말것) 처럼 두 인자를 주어야한다. 또한 수정을 할 때는 제일 마지막에 .execute()을 붙여야 수정이 완료된다. (그것을 까먹어서 꽤 오랜시간을 낭비했다...)</p>
<hr>
<p>거의 이틀을 쏟아부은 기능이었다... 코드의 흐름을 만드는 것부터 막막했는데 설마했던 이 기능이 발목을 잡을 줄은 몰랐다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 69일차 (20240403)]]></title>
            <link>https://velog.io/@park-sy13/TIL-69%EC%9D%BC%EC%B0%A8-20240403</link>
            <guid>https://velog.io/@park-sy13/TIL-69%EC%9D%BC%EC%B0%A8-20240403</guid>
            <pubDate>Wed, 03 Apr 2024 13:19:59 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/park-sy13/post/5a8668ec-2634-42af-8796-782b8c371392/image.png" alt="">
오늘 한 일</p>
<ul>
<li>interestGenre 생성 시 genre_id 못 받아오는 부분 수정</li>
<li>redis에 refresh token 넣는 기능 완성</li>
</ul>
<p>오늘 머리 싸매는 일</p>
<ul>
<li>유저 기능 수정 중 닉네임, 비밀번호, 관심 장르를 각각 따로 수정하도록 api를 나눴는데 닉네임과 비밀번호는 이전과 동일하게 할 수 있어서 빠르게 완성했으나 관심 장르는 외래키 (user_id)로 찾고 또 기존거를 유지하면 db에 양이 너무 많아질까봐 삭제 후 새 생성을 할까하는데 그 찾아오는 것이 너무 어렵다... createqureybuilder 문법 너무 어려운 것 .·´¯<code>(&gt;▂&lt;)´¯</code>·. </li>
</ul>
<hr>
<h3 id="interestgenre-생성하는-코드">interestGenre 생성하는 코드</h3>
<pre><code>// interestGenre 하나씩 생성하기
    await interestGenre.map(async element =&gt; {
      // 장르 아이디로 장르 테이블 가져오기
      const inputGenre = await this.findGenre(+element);
      if (!inputGenre) {
        throw new NotFoundException(&#39;해당 아이디의 장르는 없습니다.&#39;);
      }
      // 위의 error로 존재하지 않은 아이디(10 이상의 숫자 아이디)를 가진 interestGenre는 생성되지 않음

      return await this.interestGenreRepository.save({
        user,
        genre: inputGenre,
      });
    });


    /* 장르 아이디로 장르 받아오는 함수 */
  private async findGenre(id: number) {
    return await this.genreRepository.findOne({ where: { id } });
  }</code></pre><p>어제와 달라진 점은 map 함수의 콜백 함수를 async로 맞춰준 것! genre_id가 계속 null로 들어갔던 이유는 repository를 조회하는 문법은 async로 해야하는데 계속 async를 안넣어줬던 것... 오늘에서야 깨달아서 다행이다;;</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 68일차 (20240402)]]></title>
            <link>https://velog.io/@park-sy13/TIL-68%EC%9D%BC%EC%B0%A8-20240402</link>
            <guid>https://velog.io/@park-sy13/TIL-68%EC%9D%BC%EC%B0%A8-20240402</guid>
            <pubDate>Tue, 02 Apr 2024 12:57:11 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/park-sy13/post/16c5769b-1d92-429f-b40d-ca4af27b549e/image.png" alt=""></p>
<p>오늘은 회원가입, 로그인과 동시에 refresh token을 redis에 넣는 것까지 진행하는 것이 목표였다.</p>
<p>우선 회원가입까지는 성공했으나 로그인에 문제가 계속 생겨서.... redis라던가 redis라던가 의존성 주입이라던가... 100퍼센트 완수하지는 못했다. 내일 이어서 할 것...</p>
<hr>
<p><img src="https://velog.velcdn.com/images/park-sy13/post/df756df2-9324-45fb-93a6-bf5a0f96165c/image.png" alt="">
첫번째 오류. 외래키를 받아올 때 관계 설정 (ManyToOne)만 한게 아니라 따로 Column을 만들었더니 위의 오류가 나왔다. (근데 이전 프로젝트에선 잘만 되었다). 칼럼이 중복되었다고 나오는데 나중에 튜터님 말씀을 들어보니 column을 따로 빼는 것과 생성하는 것 각각이 장단점이 있다고 하셨다... 아무튼 위의 오류는 중복된 외래키 아이디 칼럼을 빼면 해결되는 문제였다. (여기서 오전 시간을 다 빼앗기고..)</p>
<hr>
<p><img src="https://velog.velcdn.com/images/park-sy13/post/4ccf9253-3a50-4868-b4f6-90f8d21446ac/image.png" alt="">
<img src="https://velog.velcdn.com/images/park-sy13/post/59e1bc03-c371-4d9c-b2fb-7ac0c0bd4086/image.png" alt="">
두 사진 모두 redis로 인해 의존성 관계를 잘 설정하지 못해 생긴 문제였다. 내가 참고했던 벨로그가 module없이 3계층으로만 진행한 프로젝트여서 내가 직접 모듈 짜보자! 해놓고 장렬하게 망했다...</p>
<p>우선 해결된 코드를 보자면
<img src="https://velog.velcdn.com/images/park-sy13/post/6597a2a9-ffcb-4313-b854-b1857c96551c/image.png" alt="">
provide와 useClass를 쓰는 것이었다! provide는 string을 받느라 따옴표로 감싸야한다고 하셨다.
그리고 redis.service.ts에</p>
<blockquote>
<p>@Inject(&#39;RedisRepository&#39;)</p>
</blockquote>
<p>을 추가했다.</p>
<p>다행히 튜터님의 도움으로 해결했지만 6시간을 버린 곳이라 너무 아쉽고... 또 문제는 작성했더니 또다른 에러가 뜨는 것이다...
<img src="https://velog.velcdn.com/images/park-sy13/post/ecb01b7a-2177-4555-b3d5-8822d7e3a603/image.png" alt="">
추측하기로는
<img src="https://velog.velcdn.com/images/park-sy13/post/c5e18d31-7b33-49bc-8bcd-8b85fec8deb2/image.png" alt="">
repository에서 set으로 등록하는 것이 문제인 것같은데
<img src="https://velog.velcdn.com/images/park-sy13/post/d40affab-c0bf-43ec-8a9a-e77fa52aefd6/image.png" alt="">
코드에서는 service에 있는 위의 코드가 문제일 거라고...
<img src="https://velog.velcdn.com/images/park-sy13/post/1962de32-1690-445c-aaa6-204e51923369/image.jpg" alt="">
제대로된 repository가 아니라 패키지를 불러오는 거라 너무 어렵다... 부디 내일 오전 중에 해결할 수 있길!!!!!!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 67일차 (20240401)]]></title>
            <link>https://velog.io/@park-sy13/TIL-67%EC%9D%BC%EC%B0%A8-20240401</link>
            <guid>https://velog.io/@park-sy13/TIL-67%EC%9D%BC%EC%B0%A8-20240401</guid>
            <pubDate>Mon, 01 Apr 2024 10:10:32 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/park-sy13/post/ee53d5c7-97b9-4808-b72e-2de9fafe018e/image.png" alt=""></p>
<h4 id="🥔-오늘-겪었던-문제">🥔 오늘 겪었던 문제</h4>
<ul>
<li>eslint와 prettier 적용 문제</li>
</ul>
<p>원인은 간단했는데 .prettierrc 파일에서 babel로 불러오는데, eslint와 충돌이 난 것이었다. 그래서 eslint도 파싱을 못해서 에러가 나고, prettier 역시 작동하지 못하고 에러만 띄우고 있던 것...</p>
<p><img src="https://velog.velcdn.com/images/park-sy13/post/02d09880-1d31-4384-94a9-5937abe7ba80/image.png" alt=""></p>
<p>오늘 5시간을 할애한 문제.... 🥹</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 66일차 (20240329)]]></title>
            <link>https://velog.io/@park-sy13/TIL-66%EC%9D%BC%EC%B0%A8-20240329</link>
            <guid>https://velog.io/@park-sy13/TIL-66%EC%9D%BC%EC%B0%A8-20240329</guid>
            <pubDate>Fri, 29 Mar 2024 12:58:02 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/park-sy13/post/557ff704-5152-45dc-a6b2-41aecbe48169/image.png" alt="">
<img src="https://velog.velcdn.com/images/park-sy13/post/c7df3936-db8f-4187-9227-835c0225ce1d/image.png" alt="">
위의 사진은 오늘 작성한 ERD. 오늘 새로 알게 된 사실은 drawSQL은 무료로 작성할 수 있는 테이블 수가 15개까지라는 점이었다... 그래서 급히 ERDcloud로 옮기게 되었다.
아무튼 우리 조는 m:n 관계를 해소하기 위해 테이블 수가 많이 늘어났다. 그래도 지지난 프로젝트 때 새벽 2시까지 머리를 굴린 보람이 있었다... 그때는 m:n 개념을 이해하겠다고 새벽 2시까지 있었으니...</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 65일차 (20240328)]]></title>
            <link>https://velog.io/@park-sy13/TIL-65%EC%9D%BC%EC%B0%A8-20240328</link>
            <guid>https://velog.io/@park-sy13/TIL-65%EC%9D%BC%EC%B0%A8-20240328</guid>
            <pubDate>Thu, 28 Mar 2024 12:04:40 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/park-sy13/post/e5b94e25-3563-4488-bb5e-443f1e8f28e3/image.png" alt="">
오늘은 팀 프로젝트의 큰 주제를 정했고, 사용할 기술과 기능을 와이어프레임을 작성하면서 보다 구체적으로 구상해나갔다.</p>
<p>오늘 몇 시간을 공들여서 작성한 와이어프레임...
<a href="https://miro.com/app/board/uXjVKbSGVSQ=/">https://miro.com/app/board/uXjVKbSGVSQ=/</a></p>
<p>어제와 주제 이외에 달라진 점은 게임 결제하는 시스템을 제외시켰다는 것이다. 보니 결제 api가 있던데 이를 최종 프로젝트에서 사용하기에는 기간이 많이 소요될 것 같고 어려울 것같기에 제외시켰다.
그리고 일반 채팅 이외에도 게임 채널을 새로 생성했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 64일차 (20240327)]]></title>
            <link>https://velog.io/@park-sy13/TIL-64%EC%9D%BC%EC%B0%A8-20240327-y4a3g04x</link>
            <guid>https://velog.io/@park-sy13/TIL-64%EC%9D%BC%EC%B0%A8-20240327-y4a3g04x</guid>
            <pubDate>Wed, 27 Mar 2024 11:20:55 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/park-sy13/post/360f71c1-d367-44f6-8a93-e1246b3ab114/image.png" alt="">
오늘은 팀원들과 어제와는 다른 주제인 &#39;게임&#39;에 대해서 논의하고 &#39;환경&#39; 주제와 마찬가지로 사용 기술, 목적, 타겟층에 대해 정리했다.
두 주제를 가지로 두 튜터님께 찾아갔는데 워낙 두리뭉실하게 작성하다보니 튜터님들께서도 한번에 이해하시기 어려웠지만, 두 분께서 집중하는 부분이 살짝 다르다는 점을 알았다.</p>
<p>처음 튜터님께서는 환경에서는 AI 이외에 흥미 있는 기술이 없으니 게임을 좀 더 추천하셨다. (실제 다른 기능이었던 런데이 기능도 매니저님께서 조언해주셨던 기능...) 덤으로 사용자가 많아지면 데이터베이스 관리의 난이도가 더 어렵다는 점을 알려주셨다. 확실히 이전에는 이용자가 두~세명 정도였는데 5명으로 확장하니 관계도가 상상만 해도 엄청 어려워졌다 ㅋ... 난이도는 어떻게든 높일 수 있는거구나...! 깨달았다.</p>
<p>다른 튜터님께서는 면접관은 완벽한 기술보다 기술을 사용한 이유에 좀 더 초점을 맞추셨고, 또 사용하는 기술의 양보다는 질에 좀 더 중점을 두셨다. 그리고 환경과 게임 중에서 환경 쪽을 선택하셨다. (주제의 창의성보다는 기술들을 보신 것같다.) 사실 처음에는 음성채팅과 화상 채팅이 있는 게임 쪽이 더 어려운 기술이 아니었나 했지만 튜터님의 조언을 보면 머신 러닝 쪽도 꽤 깊이 들어갈 수 있는 기술이 아닌가 살짝 흔들렸다.</p>
<hr>
<p>튜터님들의 조언을 받고 &#39;유저 플로우&#39;를 작성했다. 사실 다들 처음 작성하는 것이라 많이 부족하고, 아마 피드백 이후 수정하지 않을까 싶다.
... 아무튼 오늘은 팀 프로젝트의 주제를 정하지 못해 완결을 제대로 못낸 듯한 하루였다. 내일은 부디 주제를 정하고, 심도 있는 기술을 다룰 수 있고 뿌듯함이 남는 팀 프로젝트를 구상하면 좋을 것같다.</p>
]]></description>
        </item>
    </channel>
</rss>