ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,45 @@
+package com.example.momogum.repository.redisRepository;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Repository;
+
+import java.time.Duration;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Repository
+@RequiredArgsConstructor
+public class RedisRepository {
+
+    private final RedisTemplate<String, Object> redisTemplate;
+
+
+    // 사용자가 조회한 postId 저장
+    public void addViewedPost(String userId, String postId) {
+        String key = generateKey(userId);
+        redisTemplate.opsForSet().add(key, postId);
+    }
+
+    // 사용자가 조회한 postId 목록 조회
+    public Set<Long> getViewedPosts(String userId) {
+        String key = generateKey(userId);
+
+        return Objects.requireNonNull(redisTemplate.opsForSet().members(key)).stream()
+                .map(Object::toString)
+                .map(Long::valueOf)
+                .collect(Collectors.toSet());
+    }
+
+    // Redis Key 생성
+    private String generateKey(String userId) {
+        return "user:" + userId + ":viewed";
+    }
+
+    //  세션 만료 시간 설정
+    public void setSessionTimeout(String userId, long timeoutMinutes) {
+        String key = generateKey(userId);
+        redisTemplate.expire(key, Duration.ofMinutes(timeoutMinutes));
+    }
+}

리뷰: `redisTemplate.opsForSet().members(key)` 이부분에서 NullPointerException이 발생할 수도 있다고 생각하는데.. .. 잘 모르지만 어떻게 생각하시는지 궁금합니다
@@ -0,0 +1,45 @@
+package com.example.momogum.repository.redisRepository;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Repository;
+
+import java.time.Duration;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Repository
+@RequiredArgsConstructor
+public class RedisRepository {
+
+    private final RedisTemplate<String, Object> redisTemplate;
+
+
+    // 사용자가 조회한 postId 저장
+    public void addViewedPost(String userId, String postId) {
+        String key = generateKey(userId);
+        redisTemplate.opsForSet().add(key, postId);
+    }
+
+    // 사용자가 조회한 postId 목록 조회
+    public Set<Long> getViewedPosts(String userId) {
+        String key = generateKey(userId);
+
+        return Objects.requireNonNull(redisTemplate.opsForSet().members(key)).stream()
+                .map(Object::toString)
+                .map(Long::valueOf)
+                .collect(Collectors.toSet());
+    }
+
+    // Redis Key 생성
+    private String generateKey(String userId) {
+        return "user:" + userId + ":viewed";
+    }
+
+    //  세션 만료 시간 설정
+    public void setSessionTimeout(String userId, long timeoutMinutes) {
+        String key = generateKey(userId);
+        redisTemplate.expire(key, Duration.ofMinutes(timeoutMinutes));
+    }
+}

리뷰: 감사합니다 !
@@ -1,20 +1,59 @@
 package com.example.momogum.config;
 
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.connection.RedisPassword;
+import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
+import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
 import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
+import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
 @Configuration
+@EnableRedisRepositories
 public class RedisConfig {
 
+    @Value("${spring.data.redis.host}")
+    private String redisHost;
+
+    @Value("${spring.data.redis.port}")
+    private int redisPort;
+
+    @Value("${spring.data.redis.password}")
+    private String redisPassword;
+
+    @Bean
+    public RedisConnectionFactory redisConnectionFactory() {
+        RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(redisHost, redisPort);
+        if (redisPassword != null && !redisPassword.isBlank()) {
+            configuration.setPassword(RedisPassword.of(redisPassword));
+        }
+        return new LettuceConnectionFactory(configuration);
+    }
+
     @Bean
-    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory connectionFactory) {
-        RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
-        redisTemplate.setConnectionFactory(connectionFactory);
+    public RedisTemplate<String, Object> redisTemplate() {
+        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
+        redisTemplate.setConnectionFactory(redisConnectionFactory());
+
+        // Key와 Value의 직렬화 설정
         redisTemplate.setKeySerializer(new StringRedisSerializer());
-        redisTemplate.setValueSerializer(new StringRedisSerializer());
+        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
+        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
+        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
+
+        redisTemplate.afterPropertiesSet();
         return redisTemplate;
     }
+
+    @Bean
+    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
+        StringRedisTemplate template = new StringRedisTemplate();
+        template.setConnectionFactory(redisConnectionFactory);
+        return template;
+    }
 }

리뷰: 가독성 뿐만 아니라 유지보수에서도 이점이 있을것 같습니다 ! 
직렬화가 필요해서 급하게 추가하다 보니 시야가 좁아졌던것 같습니다 충고 감사합니다 !
@@ -2,11 +2,24 @@
 
 import com.example.momogum.domain.MealDiary;
 import com.example.momogum.domain.UserEntity;
+import com.example.momogum.domain.common.enums.IsRevisit;
+import io.lettuce.core.dynamic.annotation.Param;
 import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
 
 import java.util.List;
 
 public interface MealDiaryRepository extends JpaRepository<MealDiary,Long> {
 
     List<MealDiary> findByUserEntity(UserEntity userEntity);
+
+    @Query("SELECT m.id FROM MealDiary m WHERE m.isRevisit = :isRevisit AND m.userEntity.Id!= :userId ORDER BY m.likesCount DESC")
+    List<Long> findAllByIsRevisit(@Param("isRevisit") IsRevisit isRevisit, @Param("userId")Long userId);
+
+    @Query("SELECT m.id FROM MealDiary m WHERE m.foodCategory = :foodCategory AND m.userEntity.Id != :userId ORDER BY m.likesCount DESC")
+    List<Long> findAllByFoodCategory(@Param("foodCategory") String foodCategory, @Param("userId")Long userId);
+
+
+    List<MealDiary> findByIdIn(List<Long> ids);
+
 }

리뷰: queryDSL을 고려를 했었는데 아무래도 조금은 후순위가 될 듯합니다 ㅜㅜ
@@ -0,0 +1,45 @@
+package com.example.momogum.repository.redisRepository;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Repository;
+
+import java.time.Duration;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Repository
+@RequiredArgsConstructor
+public class RedisRepository {
+
+    private final RedisTemplate<String, Object> redisTemplate;
+
+
+    // 사용자가 조회한 postId 저장
+    public void addViewedPost(String userId, String postId) {
+        String key = generateKey(userId);
+        redisTemplate.opsForSet().add(key, postId);
+    }
+
+    // 사용자가 조회한 postId 목록 조회
+    public Set<Long> getViewedPosts(String userId) {
+        String key = generateKey(userId);
+
+        return Objects.requireNonNull(redisTemplate.opsForSet().members(key)).stream()
+                .map(Object::toString)
+                .map(Long::valueOf)
+                .collect(Collectors.toSet());
+    }
+
+    // Redis Key 생성
+    private String generateKey(String userId) {
+        return "user:" + userId + ":viewed";
+    }
+
+    //  세션 만료 시간 설정
+    public void setSessionTimeout(String userId, long timeoutMinutes) {
+        String key = generateKey(userId);
+        redisTemplate.expire(key, Duration.ofMinutes(timeoutMinutes));
+    }
+}

리뷰: 현재 검증이나 @annotation 부분에 대해서는 추가적인 mealdiary 수정 사항 반영해서 진행할려고 계획중입니다.
조금씩 변동사항이 있어 추후 한번에 검증 단계에 필요한 부분을 모아둘려고 하고 있습니다 !
@@ -1,14 +1,77 @@
-import React from "react";
-import "./PostAdd.scss";
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import './PostAdd.scss';
+import { Button } from '../PostAdd/components/Post.jsx';
 
 const PostAdd = () => {
+  const [comment, setComment] = useState('');
 
-return(
-  <div className="postAdd">
-    <div>로그인</div>
-  </div>
-);
+  const navigate = useNavigate();
 
-};
+  const handleText = value => {
+    setComment(value);
+  };
+  console.log(handleText);
+
+  const cancel = () => {
+    const confirmed = window.confirm(
+      '적으신 글을 취소하고 post-list로 넘어가겠습니까?',
+    );
+    if (confirmed) {
+      navigate('/post-list');
+    }
+  };
+
+  const posting = () => {
+    if (comment.length >= 1) {
+      navigate('/post-list');
 
-export default PostAdd;
\ No newline at end of file
+      fetch({
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json;charset=utf-8',
+        },
+        body: JSON.stringify({
+          content: '유저가 작성한 글',
+        }),
+      })
+        .then(response => response.json)
+        .then(data => console.log(data));
+    } else {
+      alert('내용을 작성주세요.');
+    }
+  };
+
+  return (
+    <div className="userBox">
+      <div className="userWrap">
+        <img className="user" src="/images/user.png" alt="userPicture" />
+        <div className="contentBox">
+          <div className="userName">NAME</div>
+          <textarea
+            onChange={event => handleText(event.target.value)}
+            className="textArea"
+            cols="80"
+            rows="30"
+            type="text"
+            value={comment}
+          />
+
+          <div className="buttonWrap">
+            <Button
+              className="customButton cancelButton"
+              buttonName="취소"
+              active={cancel}
+            />
+            <Button
+              className="customButton addButton"
+              buttonName="게시"
+              active={posting}
+            />
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+};
+export default PostAdd;

리뷰: import 순서에도 유지보수 및 가독성을 위한 컨벤션이 있습니다.
위코드의 컨벤션은 간략하게 아래와 같고, 참고해서 수정해 주세요!

- 라이브러리
    - React 관련 패키지
    - 외부 라이브러리
- 컴포넌트
    - 공통 컴포넌트 → 먼 컴포넌트 → 가까운 컴포넌트
- 함수, 변수 및 설정 파일
- 사진 등 미디어 파일(`.png`)
- css 파일 (.`scss`)
@@ -1,14 +1,77 @@
-import React from "react";
-import "./PostAdd.scss";
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import './PostAdd.scss';
+import { Button } from '../PostAdd/components/Post.jsx';
 
 const PostAdd = () => {
+  const [comment, setComment] = useState('');
 
-return(
-  <div className="postAdd">
-    <div>로그인</div>
-  </div>
-);
+  const navigate = useNavigate();
 
-};
+  const handleText = value => {
+    setComment(value);
+  };
+  console.log(handleText);
+
+  const cancel = () => {
+    const confirmed = window.confirm(
+      '적으신 글을 취소하고 post-list로 넘어가겠습니까?',
+    );
+    if (confirmed) {
+      navigate('/post-list');
+    }
+  };
+
+  const posting = () => {
+    if (comment.length >= 1) {
+      navigate('/post-list');
 
-export default PostAdd;
\ No newline at end of file
+      fetch({
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json;charset=utf-8',
+        },
+        body: JSON.stringify({
+          content: '유저가 작성한 글',
+        }),
+      })
+        .then(response => response.json)
+        .then(data => console.log(data));
+    } else {
+      alert('내용을 작성주세요.');
+    }
+  };
+
+  return (
+    <div className="userBox">
+      <div className="userWrap">
+        <img className="user" src="/images/user.png" alt="userPicture" />
+        <div className="contentBox">
+          <div className="userName">NAME</div>
+          <textarea
+            onChange={event => handleText(event.target.value)}
+            className="textArea"
+            cols="80"
+            rows="30"
+            type="text"
+            value={comment}
+          />
+
+          <div className="buttonWrap">
+            <Button
+              className="customButton cancelButton"
+              buttonName="취소"
+              active={cancel}
+            />
+            <Button
+              className="customButton addButton"
+              buttonName="게시"
+              active={posting}
+            />
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+};
+export default PostAdd;

리뷰: event 객체에는 여러 데이터가 담겨있습니다.
그 중에서 target의 value(input에 입력한 값)만을 사용하기 때문에, 함수를 호출할 때 인자로 아예 event.target.value만을 인자로 넘겨서 사용하면 이 함수의 사용이 좀 더 명확하겠네요!
@@ -1,14 +1,77 @@
-import React from "react";
-import "./PostAdd.scss";
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import './PostAdd.scss';
+import { Button } from '../PostAdd/components/Post.jsx';
 
 const PostAdd = () => {
+  const [comment, setComment] = useState('');
 
-return(
-  <div className="postAdd">
-    <div>로그인</div>
-  </div>
-);
+  const navigate = useNavigate();
 
-};
+  const handleText = value => {
+    setComment(value);
+  };
+  console.log(handleText);
+
+  const cancel = () => {
+    const confirmed = window.confirm(
+      '적으신 글을 취소하고 post-list로 넘어가겠습니까?',
+    );
+    if (confirmed) {
+      navigate('/post-list');
+    }
+  };
+
+  const posting = () => {
+    if (comment.length >= 1) {
+      navigate('/post-list');
 
-export default PostAdd;
\ No newline at end of file
+      fetch({
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json;charset=utf-8',
+        },
+        body: JSON.stringify({
+          content: '유저가 작성한 글',
+        }),
+      })
+        .then(response => response.json)
+        .then(data => console.log(data));
+    } else {
+      alert('내용을 작성주세요.');
+    }
+  };
+
+  return (
+    <div className="userBox">
+      <div className="userWrap">
+        <img className="user" src="/images/user.png" alt="userPicture" />
+        <div className="contentBox">
+          <div className="userName">NAME</div>
+          <textarea
+            onChange={event => handleText(event.target.value)}
+            className="textArea"
+            cols="80"
+            rows="30"
+            type="text"
+            value={comment}
+          />
+
+          <div className="buttonWrap">
+            <Button
+              className="customButton cancelButton"
+              buttonName="취소"
+              active={cancel}
+            />
+            <Button
+              className="customButton addButton"
+              buttonName="게시"
+              active={posting}
+            />
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+};
+export default PostAdd;

리뷰: 실수로 눌렀을 경우에도 무조건 list 페이지로 이동하게 될 텐데, 선택권을 줄 수도 있겠네요.

`window.confirm` 함수에 대해 찾아보고 적용해보셔도 좋을 것 같습니다!
@@ -1,14 +1,77 @@
-import React from "react";
-import "./PostAdd.scss";
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import './PostAdd.scss';
+import { Button } from '../PostAdd/components/Post.jsx';
 
 const PostAdd = () => {
+  const [comment, setComment] = useState('');
 
-return(
-  <div className="postAdd">
-    <div>로그인</div>
-  </div>
-);
+  const navigate = useNavigate();
 
-};
+  const handleText = value => {
+    setComment(value);
+  };
+  console.log(handleText);
+
+  const cancel = () => {
+    const confirmed = window.confirm(
+      '적으신 글을 취소하고 post-list로 넘어가겠습니까?',
+    );
+    if (confirmed) {
+      navigate('/post-list');
+    }
+  };
+
+  const posting = () => {
+    if (comment.length >= 1) {
+      navigate('/post-list');
 
-export default PostAdd;
\ No newline at end of file
+      fetch({
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json;charset=utf-8',
+        },
+        body: JSON.stringify({
+          content: '유저가 작성한 글',
+        }),
+      })
+        .then(response => response.json)
+        .then(data => console.log(data));
+    } else {
+      alert('내용을 작성주세요.');
+    }
+  };
+
+  return (
+    <div className="userBox">
+      <div className="userWrap">
+        <img className="user" src="/images/user.png" alt="userPicture" />
+        <div className="contentBox">
+          <div className="userName">NAME</div>
+          <textarea
+            onChange={event => handleText(event.target.value)}
+            className="textArea"
+            cols="80"
+            rows="30"
+            type="text"
+            value={comment}
+          />
+
+          <div className="buttonWrap">
+            <Button
+              className="customButton cancelButton"
+              buttonName="취소"
+              active={cancel}
+            />
+            <Button
+              className="customButton addButton"
+              buttonName="게시"
+              active={posting}
+            />
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+};
+export default PostAdd;

리뷰: fetch 함수의 인자는 보통 2개가 들어가는데, 지금은 옵션에 관련된 두 번째 인자만 작성되어 있네요!
실제로 통신하실 때에는 첫 번째 인자인 `API` 값도 잘 전달해 주세요!
@@ -1,14 +1,77 @@
-import React from "react";
-import "./PostAdd.scss";
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import './PostAdd.scss';
+import { Button } from '../PostAdd/components/Post.jsx';
 
 const PostAdd = () => {
+  const [comment, setComment] = useState('');
 
-return(
-  <div className="postAdd">
-    <div>로그인</div>
-  </div>
-);
+  const navigate = useNavigate();
 
-};
+  const handleText = value => {
+    setComment(value);
+  };
+  console.log(handleText);
+
+  const cancel = () => {
+    const confirmed = window.confirm(
+      '적으신 글을 취소하고 post-list로 넘어가겠습니까?',
+    );
+    if (confirmed) {
+      navigate('/post-list');
+    }
+  };
+
+  const posting = () => {
+    if (comment.length >= 1) {
+      navigate('/post-list');
 
-export default PostAdd;
\ No newline at end of file
+      fetch({
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json;charset=utf-8',
+        },
+        body: JSON.stringify({
+          content: '유저가 작성한 글',
+        }),
+      })
+        .then(response => response.json)
+        .then(data => console.log(data));
+    } else {
+      alert('내용을 작성주세요.');
+    }
+  };
+
+  return (
+    <div className="userBox">
+      <div className="userWrap">
+        <img className="user" src="/images/user.png" alt="userPicture" />
+        <div className="contentBox">
+          <div className="userName">NAME</div>
+          <textarea
+            onChange={event => handleText(event.target.value)}
+            className="textArea"
+            cols="80"
+            rows="30"
+            type="text"
+            value={comment}
+          />
+
+          <div className="buttonWrap">
+            <Button
+              className="customButton cancelButton"
+              buttonName="취소"
+              active={cancel}
+            />
+            <Button
+              className="customButton addButton"
+              buttonName="게시"
+              active={posting}
+            />
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+};
+export default PostAdd;

리뷰: 불필요하게 3중으로 감싸고 있는 것 같은데, 일단 최상위의 div는 불필요해 보이네요!

요소가 중첩될 수록, 프로젝트의 규모가 커질 수록 로딩되는 시간이 길어지기 때문에 불필요하게 중첩된 요소는 지워주셔야 합니다!
@@ -1,14 +1,77 @@
-import React from "react";
-import "./PostAdd.scss";
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import './PostAdd.scss';
+import { Button } from '../PostAdd/components/Post.jsx';
 
 const PostAdd = () => {
+  const [comment, setComment] = useState('');
 
-return(
-  <div className="postAdd">
-    <div>로그인</div>
-  </div>
-);
+  const navigate = useNavigate();
 
-};
+  const handleText = value => {
+    setComment(value);
+  };
+  console.log(handleText);
+
+  const cancel = () => {
+    const confirmed = window.confirm(
+      '적으신 글을 취소하고 post-list로 넘어가겠습니까?',
+    );
+    if (confirmed) {
+      navigate('/post-list');
+    }
+  };
+
+  const posting = () => {
+    if (comment.length >= 1) {
+      navigate('/post-list');
 
-export default PostAdd;
\ No newline at end of file
+      fetch({
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json;charset=utf-8',
+        },
+        body: JSON.stringify({
+          content: '유저가 작성한 글',
+        }),
+      })
+        .then(response => response.json)
+        .then(data => console.log(data));
+    } else {
+      alert('내용을 작성주세요.');
+    }
+  };
+
+  return (
+    <div className="userBox">
+      <div className="userWrap">
+        <img className="user" src="/images/user.png" alt="userPicture" />
+        <div className="contentBox">
+          <div className="userName">NAME</div>
+          <textarea
+            onChange={event => handleText(event.target.value)}
+            className="textArea"
+            cols="80"
+            rows="30"
+            type="text"
+            value={comment}
+          />
+
+          <div className="buttonWrap">
+            <Button
+              className="customButton cancelButton"
+              buttonName="취소"
+              active={cancel}
+            />
+            <Button
+              className="customButton addButton"
+              buttonName="게시"
+              active={posting}
+            />
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+};
+export default PostAdd;

리뷰: img 태그의 alt 값은 어디에 활용될까요?
찾아보시고 올바르게 작성해 주세요!
@@ -0,0 +1,79 @@
+.userBox {
+  margin: 20px;
+  gap: 0 24px;
+  display: flex;
+  justify-content: center;
+  flex-direction: column;
+
+  .userWrap {
+    display: flex;
+    flex-direction: row;
+    justify-content: center;
+
+    .user {
+      width: 50px;
+      height: 50px;
+      border-radius: 50%;
+    }
+
+    .contentBox {
+      display: flex;
+      flex-direction: column;
+      gap: 10px;
+      margin-left: 20px;
+
+      .userName {
+        font-size: 20px;
+        font-weight: 700;
+      }
+
+      .textArea {
+        padding: 10px;
+        border-radius: 6px;
+        border: 1px solid var(--grey-88, #e0e0e0);
+        background: var(--white, #fff);
+        resize: none;
+        transition: border-color 0.3s;
+        outline: none;
+      }
+      .textArea:hover {
+        border-color: var(--gray, #999999);
+      }
+
+      .buttonWrap {
+        justify-content: space-between;
+        display: flex;
+
+        .customButton {
+          width: 120px;
+          height: 50px;
+          border-radius: 6px;
+        }
+
+        .cancelButton {
+          border: 1px solid var(--blue, #2d71f7);
+          background: var(--white, #fff);
+          color: var(--blue, #2d71f7);
+          cursor: pointer;
+        }
+        .cancelButton:hover {
+          border: 1px solid var(--navy, #083e7f);
+          background: var(--white, #fff);
+          color: var(--navy, #083e7f);
+        }
+
+        .addButton {
+          border: none;
+          background: var(--white, #2d71f7);
+          color: var(--white, #fff);
+          cursor: pointer;
+        }
+        .addButton:hover {
+          border: none;
+          background: var(--blue, #083e7f);
+          color: var(--white, #fff);
+        }
+      }
+    }
+  }
+}

리뷰: PR이 머지된다면, 이 프로젝트의 모든 button 태그에 `width: 120px, height: 50px` 속성이 적용되겠네요!
이를 방지하기 위해서는 위에 잘 해주신 것처럼 nesting되어야 합니다.

추가로, 태그 선택자의 사용은 다음의 경우 외에는 지양하는 게 좋습니다.
1. 전역에 적용되어야 하는 스타일 (e.g. common.scss, reset.scss)
2. 앞으로도 변경되지 않는다는 확신이 있는 요소

className을 부여해서 스타일 속성 부여해 주세요!