ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,111 @@
+import { MENU, MESSAGE, ARRAY, CALENDAR, BENEFIT_AMOUNT, BADGE, BOUNDARY } from "../commons/constants.js";
+import Order from "./Order.js";
+import { getMenu, getMenuCount } from "../commons/utils.js";
+
+class Benefit {
+  #date;
+  #menu;
+  #order;
+  #benefitResult;
+
+  constructor(date, menu) {
+    this.#date = date;
+    this.#menu = menu;
+    this.#order = new Order(date, menu);
+  }
+
+  // 혜택 내역
+  benefitDetail() {
+    // benefitResult [ DDay | Weekday | Weekend | Special | Giveaway ]
+    this.#benefitResult = [0, 0, 0, 0, 0];
+
+    if (this.#isBenefit()) {
+      if (this.#isDDay()) this.#benefitResult[ARRAY.DDAY] = this.#applyDDay();
+      if (this.#isWeekday()) this.#benefitResult[ARRAY.WEEKDAY] = this.#applyWeekday();
+      if (this.#isWeekend()) this.#benefitResult[ARRAY.WEEKEND] = this.#applyWeekend();
+      if (this.#isSpecial()) this.#benefitResult[ARRAY.SPECIALDAY] = this.#applySpecial();
+      if (this.#isGiveaway()) this.#benefitResult[ARRAY.GIVEAWAY] = this.#applyGiveaway();
+    }
+
+    return this.#benefitResult;
+  }
+
+  // 총 혜택 금액
+  totalBenefit() {
+    let totalBenefit = 0;
+    this.#benefitResult.forEach((benefit) => {
+      totalBenefit += benefit;
+    });
+
+    return totalBenefit;
+  }
+
+  // 할인 후 예상 결제 금액
+  expectedAmount() {
+    const totalDiscount = this.totalBenefit() - this.#benefitResult[ARRAY.GIVEAWAY];
+
+    return this.#order.getAllAmount() - totalDiscount;
+  }
+
+  // 12월 이벤트 배지
+  eventBadge() {
+    const totalBenefit = this.totalBenefit();
+
+    if (!this.#isBenefit()) return MESSAGE.NOTHING;
+    if (totalBenefit <= BOUNDARY.STAR_PRICE) return BADGE.STAR;
+    if (totalBenefit <= BOUNDARY.TREE_PRICE) return BADGE.TREE;
+
+    return BADGE.SANTA;
+  }
+
+  #isBenefit() {
+    return this.#order.getAllAmount() > BOUNDARY.BENEFIT_PRICE;
+  }
+
+  #isDDay() {
+    return this.#date <= BOUNDARY.DDAY_END;
+  }
+
+  #isWeekday() {
+    return CALENDAR.WEEKDAY.includes(this.#date);
+  }
+
+  #isWeekend() {
+    return CALENDAR.WEEKEND.includes(this.#date);
+  }
+
+  #isSpecial() {
+    return CALENDAR.SPECIAL.includes(this.#date);
+  }
+
+  #isGiveaway() {
+    return this.#order.getAllAmount() >= BOUNDARY.GIVEAWAY_PRICE;
+  }
+
+  #applyDDay() {
+    let discount = BENEFIT_AMOUNT.DDAY_START;
+    for (let day = 1; day < this.#date; day++) {
+      discount += BENEFIT_AMOUNT.DDAY_PLUS;
+    }
+    return discount;
+  }
+
+  #applyWeekday() {
+    const menuCount = getMenuCount(getMenu(MENU.DESSERT), this.#menu);
+    return menuCount * BENEFIT_AMOUNT.WEEKDAY;
+  }
+
+  #applyWeekend() {
+    const menuCount = getMenuCount(getMenu(MENU.MAIN), this.#menu);
+    return menuCount * BENEFIT_AMOUNT.WEEKEND;
+  }
+
+  #applySpecial() {
+    return BENEFIT_AMOUNT.SPECIALDAY;
+  }
+
+  #applyGiveaway() {
+    return BENEFIT_AMOUNT.GIVEAWAY;
+  }
+}
+export default Benefit;

리뷰: 클래스 분리에 너무 집중하느라 
혜택을 확인하는 클래스와 혜택을 적용하는 클래스로 분리해서 작성했었는데,
하나의 클래스에 묶어서 작성하는 것도 괜찮아 보이네요😂
@@ -0,0 +1,63 @@
+import InputView from "../view/InputView.js";
+import OutputView from "../view/OutputView.js";
+import Order from "../model/Order.js";
+import Benefit from "../model/Benefit.js";
+import { print } from "../commons/utils.js";
+import { MENU } from "../commons/constants.js";
+
+class EventController {
+  #date;
+  #menu;
+
+  async eventStart() {
+    OutputView.printStart();
+
+    // 주문 정보 입력 & 주문 정보 출력
+    await this.#getOrderInformation();
+    OutputView.printOrderInformation(this.#date, this.#menu);
+
+    // 주문 클래스 생성 & 총 주문 금액 출력
+    const order = new Order(this.#date, this.#menu);
+    OutputView.printAllAmount(order.getAllAmount());
+
+    // 혜택 클래스 생성 & 혜택과 관련된 메세지 출력
+    const benefit = new Benefit(this.#date, this.#menu);
+    this.#printBenefit(benefit);
+  }
+
+  async #getOrderInformation() {
+    await this.#getDate();
+    await this.#getMenu();
+  }
+
+  async #getDate() {
+    while (true) {
+      try {
+        this.#date = await InputView.inputDate();
+        break;
+      } catch (error) {
+        print(error);
+      }
+    }
+  }
+
+  async #getMenu() {
+    while (true) {
+      try {
+        this.#menu = await InputView.inputMenu();
+        break;
+      } catch (error) {
+        print(error);
+      }
+    }
+  }
+
+  #printBenefit(benefit) {
+    OutputView.printGiveaway(benefit.benefitDetail()); // <증정 메뉴>
+    OutputView.printBenefitDetail(benefit.benefitDetail()); // <혜택 내역>
+    OutputView.printTotalBenefit(benefit.totalBenefit()); // <총 혜텍 금액>
+    OutputView.printExpectedAmount(benefit.expectedAmount()); // <할인 후 예상 결제 금액>
+    OutputView.printEventBadge(benefit.eventBadge()); // <12월 이벤트 배지>
+  }
+}
+export default EventController;

리뷰: Controller의 필드는 model의 필드를 통해 불러올 수 있다고 생각합니다!
필드를 줄여보는건 어떨까요?
@@ -0,0 +1,63 @@
+import InputView from "../view/InputView.js";
+import OutputView from "../view/OutputView.js";
+import Order from "../model/Order.js";
+import Benefit from "../model/Benefit.js";
+import { print } from "../commons/utils.js";
+import { MENU } from "../commons/constants.js";
+
+class EventController {
+  #date;
+  #menu;
+
+  async eventStart() {
+    OutputView.printStart();
+
+    // 주문 정보 입력 & 주문 정보 출력
+    await this.#getOrderInformation();
+    OutputView.printOrderInformation(this.#date, this.#menu);
+
+    // 주문 클래스 생성 & 총 주문 금액 출력
+    const order = new Order(this.#date, this.#menu);
+    OutputView.printAllAmount(order.getAllAmount());
+
+    // 혜택 클래스 생성 & 혜택과 관련된 메세지 출력
+    const benefit = new Benefit(this.#date, this.#menu);
+    this.#printBenefit(benefit);
+  }
+
+  async #getOrderInformation() {
+    await this.#getDate();
+    await this.#getMenu();
+  }
+
+  async #getDate() {
+    while (true) {
+      try {
+        this.#date = await InputView.inputDate();
+        break;
+      } catch (error) {
+        print(error);
+      }
+    }
+  }
+
+  async #getMenu() {
+    while (true) {
+      try {
+        this.#menu = await InputView.inputMenu();
+        break;
+      } catch (error) {
+        print(error);
+      }
+    }
+  }
+
+  #printBenefit(benefit) {
+    OutputView.printGiveaway(benefit.benefitDetail()); // <증정 메뉴>
+    OutputView.printBenefitDetail(benefit.benefitDetail()); // <혜택 내역>
+    OutputView.printTotalBenefit(benefit.totalBenefit()); // <총 혜텍 금액>
+    OutputView.printExpectedAmount(benefit.expectedAmount()); // <할인 후 예상 결제 금액>
+    OutputView.printEventBadge(benefit.eventBadge()); // <12월 이벤트 배지>
+  }
+}
+export default EventController;

리뷰: ERROR의 출력도 util 이 아닌 OutputView 에서 해주는건 어떨까요?
@@ -0,0 +1,56 @@
+import { print, priceToString } from "../commons/utils.js";
+import { MESSAGE, ARRAY, BENEFIT_AMOUNT } from "../commons/constants.js";
+
+const OutputView = {
+  printStart() {
+    print(MESSAGE.START);
+  },
+
+  printOrderInformation(date, menu) {
+    print(MESSAGE.MONTH + date + MESSAGE.SHOW_BENEFIT);
+    print(MESSAGE.MENU);
+    menu[ARRAY.MENU].forEach((menuName, index) => {
+      print(menuName + MESSAGE.BLANK + menu[ARRAY.COUNT][index] + MESSAGE.EA);
+    });
+  },
+
+  printAllAmount(amount) {
+    print(MESSAGE.TOTAL_AMOUNT);
+    print(priceToString(amount) + MESSAGE.WON);
+  },
+
+  printGiveaway(benefitResult) {
+    print(MESSAGE.GIVEAWAY);
+    if (benefitResult[ARRAY.GIVEAWAY] !== 0) print(MESSAGE.GIVEAWAY_ITEM);
+    else print(MESSAGE.NOTHING);
+  },
+
+  printBenefitDetail(benefitResult) {
+    print(MESSAGE.BENEFIT_DETAILS);
+    if (JSON.stringify(benefitResult) === "[0,0,0,0,0]") {
+      print(MESSAGE.NOTHING);
+    } else {
+      benefitResult.forEach((benefit, index) => {
+        if (benefit !== 0) print(MESSAGE.BENEFIT_DETAILS_TITLE[index] + priceToString(benefit) + MESSAGE.WON);
+      });
+    }
+  },
+
+  printTotalBenefit(totalBenefit) {
+    print(MESSAGE.TOTAL_BENEFIT);
+    if (totalBenefit > 0) print(MESSAGE.HYPEN + priceToString(totalBenefit) + MESSAGE.WON);
+    else print(priceToString(totalBenefit) + MESSAGE.WON);
+  },
+
+  printExpectedAmount(expectedAmount) {
+    print(MESSAGE.EXPECTED_AMOUNT);
+    print(priceToString(expectedAmount) + MESSAGE.WON);
+  },
+
+  printEventBadge(eventBadge) {
+    print(MESSAGE.EVENT_BADGE);
+    print(eventBadge);
+  },
+};
+
+export default OutputView;

리뷰: else 를 지양하라는 요구사항이 있어서,
위에 if문에 return 해주는 방식을 추천합니다!
@@ -2,6 +2,8 @@
 
 import React, { useState } from "react";
 
+// 테스트
+
 export interface ButtonProps {
   label: string;
   onClick: () => void;

리뷰: 코드를 살펴보았습니다. 여기 몇 가지 코멘트를 드리겠습니다.

1. **주석 사용**: 추가한 `// 테스트` 주석은 의미가 불분명합니다. 주석은 코드의 의도를 명확히 하는 데 도움이 되어야 하므로, 왜 테스트 주석이 필요한지 구체적으로 설명하는 것이 좋습니다. 예를 들어, `// 버튼 테스트를 위해 추가된 주석`과 같이 작성하면 좋습니다.

2. **타입 정의**: `ButtonProps` 인터페이스는 잘 정의되어 있습니다. 하지만 사용되는 컴포넌트가 실제로 있는 경우 이를 추가적으로 보여주면 코드가 더욱 완전해질 수 있습니다. 예를 들어, `Button` 컴포넌트가 어떻게 `ButtonProps`를 사용하는지 보여주면 좋습니다.

3. **주석 위치**: 코드의 상단에 주석을 추가하는 것은 유용할 수 있지만, 더 많은 정보가 필요한 경우 주석이 제대로 설명되는 것이 중요합니다. 필요한 부분에 대해 더 구체적으로 설명해 주세요.

추가 코드나 특정 기능에 대한 설명이 있다면 더 나은 피드백을 제공할 수 있을 것입니다. 필요하시면 계속 말씀해 주세요!
@@ -0,0 +1,88 @@
+import { createGlobalStyle } from 'styled-components'
+import reset from 'styled-reset'
+
+const MainStyles = createGlobalStyle` 
+    .relative{position:relative}
+    .absolute{position:absolute}
+    .fixed{position:fixed}
+    
+    .flex-center-between {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+    }
+    .flex-center-center {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+    }
+    .flex-center-start {
+        display: flex;
+        align-items: center;
+        justify-content: flex-start;
+    }
+    .gap-8{gap:8px}
+    .gap-9{gap:9px}
+    .gap-10{gap:10px}
+    .gap-20{gap:20px}
+    .gap-21{gap:21px}
+    .gap-22{gap:22px}
+    .gap-23{gap:23px}
+    .gap-24{gap:24px}
+    .gap-25{gap:25px}
+    .gap-26{gap:26px}
+    .gap-27{gap:27px}
+    .gap-28{gap:28px}
+    .gap-29{gap:29px}
+    .gap-30{gap:30px}
+    .gap-40{gap:40px}
+    .gap-50{gap:50px}
+    .gap-60{gap:60px}
+    .gap-70{gap:70px}
+    .gap-80{gap:80px}
+    .gap-90{gap:90px}
+    .width-100{width:100%}
+    .max-width{width:max-content}
+    .over-hidden{overflow: scroll}
+
+    .margint-tb-10{margin:10px 0}
+    .margint-tb-11{margin:11px 0}
+    .margint-tb-12{margin:12px 0}
+    .margint-tb-13{margin:13px 0}
+    .margint-tb-14{margin:14px 0}
+    .margint-tb-15{margin:15px 0}
+    .margint-tb-16{margin:16px 0}
+    .margint-tb-17{margin:17px 0}
+    .margint-tb-18{margin:18px 0}
+    .margint-tb-19{margin:19px 0}
+    .margint-tb-20{margin:10px 0}
+
+    .margin-b-10{margin-bottom:10px}
+    .margin-b-11{margin-bottom:11px}
+    .margin-b-12{margin-bottom:12px}
+    .margin-b-13{margin-bottom:13px}
+    .margin-b-14{margin-bottom:14px}
+    .margin-b-15{margin-bottom:15px}
+    .margin-b-16{margin-bottom:16px}
+    .margin-b-17{margin-bottom:17px}
+    .margin-b-18{margin-bottom:18px}
+    .margin-b-19{margin-bottom:19px}
+    .margin-b-20{margin-bottom:20px}
+
+    .padding-lr-10{padding:0 10px}
+    .padding-lr-11{padding:0 11px}
+    .padding-lr-12{padding:0 12px}
+    .padding-lr-13{padding:0 13px}
+    .padding-lr-14{padding:0 14px}
+    .padding-lr-15{padding:0 15px}
+    .padding-lr-16{padding:0 16px}
+    .padding-lr-17{padding:0 17px}
+    .padding-lr-18{padding:0 18px}
+    .padding-lr-19{padding:0 19px}
+    .padding-lr-20{padding:0 20px}
+
+    .bottom-0{bottom:0}
+  
+`
+
+export default MainStyles

리뷰: 이렇게 쓰고 싶다면, 관련 `styles/mixin.ts`에 mixin 함수를 만들 수 있을 것 같습니다.

// flex관련 mixin
```suggestion

   interface FlexMixin={
   justifyContent?:'center' | 'flex-end' | 'flex-start' | 'space-evenly'
   alignItems? :'center' | 'flex-end' 
   }
   export const MixinFlex({ alignItems,justifyContent }  : FlexMixin)  {  
   
          return css`
          
         display:flex;
         ${justifyContent && css` justify-content : ${justifyContent}`}
        ${alignItems && css`align-Items : ${alignItems}`}
          `
   
   }
```

이런식으로 확장성있게 개발이 가능합니다!
cc. @eunbae11
@@ -0,0 +1,35 @@
+package db;
+
+import db.HttpSessions;
+import model.User;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import webserver.domain.Session;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class HttpSessionsTest {
+
+    @Test
+    @DisplayName("세션 일치 테스트")
+    void matchTest() {
+        User user = new User("dino", "dino", "dino", "dino.bin@kakaocorp.com");
+        String id = HttpSessions.getId();
+        Session session = new Session();
+        session.addAttribute("user", user);
+        HttpSessions.addSession(id, session);
+        assertThat(HttpSessions.getSession(id).getAttribute("user")).isEqualTo(user);
+    }
+
+    @Test
+    @DisplayName("세션 불일치 테스트")
+    void unMatchTest() {
+        User user = new User("dino", "dino", "dino", "dino.bin@kakaocorp.com");
+        String id = HttpSessions.getId();
+        Session session = new Session();
+        session.addAttribute("user", user);
+        HttpSessions.addSession(id, session);
+        String id2 = HttpSessions.getId();
+        assertThat(HttpSessions.getSession(id2)).isNotEqualTo(HttpSessions.getSession(id));
+    }
+}

리뷰: 테스트도 Session 추가시에 같이 해주셨네요. 👍
@@ -0,0 +1,35 @@
+package db;
+
+import db.HttpSessions;
+import model.User;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import webserver.domain.Session;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class HttpSessionsTest {
+
+    @Test
+    @DisplayName("세션 일치 테스트")
+    void matchTest() {
+        User user = new User("dino", "dino", "dino", "dino.bin@kakaocorp.com");
+        String id = HttpSessions.getId();
+        Session session = new Session();
+        session.addAttribute("user", user);
+        HttpSessions.addSession(id, session);
+        assertThat(HttpSessions.getSession(id).getAttribute("user")).isEqualTo(user);
+    }
+
+    @Test
+    @DisplayName("세션 불일치 테스트")
+    void unMatchTest() {
+        User user = new User("dino", "dino", "dino", "dino.bin@kakaocorp.com");
+        String id = HttpSessions.getId();
+        Session session = new Session();
+        session.addAttribute("user", user);
+        HttpSessions.addSession(id, session);
+        String id2 = HttpSessions.getId();
+        assertThat(HttpSessions.getSession(id2)).isNotEqualTo(HttpSessions.getSession(id));
+    }
+}

리뷰: 불일치까지 👍
@@ -0,0 +1,35 @@
+package db;
+
+import db.HttpSessions;
+import model.User;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import webserver.domain.Session;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class HttpSessionsTest {
+
+    @Test
+    @DisplayName("세션 일치 테스트")
+    void matchTest() {
+        User user = new User("dino", "dino", "dino", "dino.bin@kakaocorp.com");
+        String id = HttpSessions.getId();
+        Session session = new Session();
+        session.addAttribute("user", user);
+        HttpSessions.addSession(id, session);
+        assertThat(HttpSessions.getSession(id).getAttribute("user")).isEqualTo(user);
+    }
+
+    @Test
+    @DisplayName("세션 불일치 테스트")
+    void unMatchTest() {
+        User user = new User("dino", "dino", "dino", "dino.bin@kakaocorp.com");
+        String id = HttpSessions.getId();
+        Session session = new Session();
+        session.addAttribute("user", user);
+        HttpSessions.addSession(id, session);
+        String id2 = HttpSessions.getId();
+        assertThat(HttpSessions.getSession(id2)).isNotEqualTo(HttpSessions.getSession(id));
+    }
+}

리뷰: 사용하지 않는 import는 제거하죠 😉
@@ -16,10 +16,10 @@ public class HttpResponse {
 
     private HttpStatusCode httpStatusCode;
     private Map<String, String> headers;
-    private List<Cookie> cookies;
+    private Cookies cookies;
     private byte[] body;
 
-    public HttpResponse(HttpStatusCode httpStatusCode, Map<String, String> headers, List<Cookie> cookies, byte[] body) {
+    public HttpResponse(HttpStatusCode httpStatusCode, Map<String, String> headers, Cookies cookies, byte[] body) {
         this.httpStatusCode = httpStatusCode;
         this.headers = headers;
         this.cookies = cookies;
@@ -43,18 +43,15 @@ private String headersList() {
                 .append(": ")
                 .append(headers.get(key))
                 .append("\r\n"));
+        sb.append(cookies.toString());
 
-        cookies.forEach(cookie -> sb.append(HttpHeader.SET_COOKIE)
-                .append(": ")
-                .append(cookie.toString())
-                .append("\r\n"));
         return sb.toString();
     }
 
     public static class Builder {
         private HttpStatusCode httpStatusCode;
         private Map<String, String> headers = new HashMap<>();
-        private List<Cookie> cookies = new ArrayList<>();
+        private Cookies cookies = new Cookies();
         private byte[] body;
 
         public Builder status(HttpStatusCode httpStatusCode) {
@@ -71,13 +68,12 @@ public Builder body(String path) throws IOException, URISyntaxException {
 
         public Builder body(String path, Map<String, Object> parameter) throws IOException {
             this.body = TemplateUtils.getTemplatePage(path, parameter);
-            logger.debug(new String(body));
             headers.put(HttpHeader.CONTENT_LENGTH, String.valueOf(this.body.length));
             return this;
         }
 
         public Builder cookie(Cookie cookie) {
-            this.cookies.add(cookie);
+            this.cookies.addCookie(cookie);
             return this;
         }
 

리뷰: 지금은 제거하셨지만 디버깅 목적으로 System.out.println이 아니라 logger.debug를 사용해주셨었네요. 💯
@@ -105,9 +105,15 @@ public String getBody() {
     }
 
     public boolean containsCookie(String cookie) {
-        return this.headers
-                .get(HttpHeader.COOKIE)
-                .contains(cookie);
+        String requestCookie = this.headers.get(HttpHeader.COOKIE);
+        if (requestCookie != null) {
+            return requestCookie.contains(cookie);
+        }
+        return false;
+    }
+
+    public Cookies getCookies() {
+        return new Cookies(headers.get(HttpHeader.COOKIE));
     }
 
     public boolean isTemplate() {

리뷰: 약간의 개인적인 선호인데 지금은 복잡하지 않아서 차이가 크지 않지만 사람은 부정보다는 긍정을 더 자연스럽게 읽는 경향이 있어서 가능하다면 긍정의 조건을 선택하는 편입니다.
```java
        if (StringUtils.hasText(requestCookie)) {
            return requestCookie.contains(cookie);
        }
        return false;
```
빈 문자열일 때는 어떻게 해야할지는 살짝 고민되네요.
@@ -0,0 +1,31 @@
+package db;
+
+import webserver.domain.Session;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+public class HttpSessions {
+    private static final Map<String, Session> sessions = new HashMap<>();
+
+    public static String getId() {
+        return UUID.randomUUID().toString();
+    }
+
+    public static void addSession(String name, Session session) {
+        sessions.put(name, session);
+    }
+
+    public static Session getSession(String name) {
+        return sessions.get(name);
+    }
+
+    public static void removeSession(String name) {
+        sessions.remove(name);
+    }
+
+    public static void invalidate() {
+        sessions.clear();
+    }
+}

리뷰: 애플리케이션 공통적으로 사용될 수 있게 DB로 사용해 주셨네요 👍
@@ -0,0 +1,31 @@
+package db;
+
+import webserver.domain.Session;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+public class HttpSessions {
+    private static final Map<String, Session> sessions = new HashMap<>();
+
+    public static String getId() {
+        return UUID.randomUUID().toString();
+    }
+
+    public static void addSession(String name, Session session) {
+        sessions.put(name, session);
+    }
+
+    public static Session getSession(String name) {
+        return sessions.get(name);
+    }
+
+    public static void removeSession(String name) {
+        sessions.remove(name);
+    }
+
+    public static void invalidate() {
+        sessions.clear();
+    }
+}

리뷰: 키 생성을 어떻게 할까 고민하다가 DB가 키생성을 하는걸 고려하여 여기 넣어 주신 것 같네요.
DB에서 보통 이와 같은 난수로 키를 만들지도 않고, HttpSessions가 아닌 다른 DB, 예를 들면 Redis를 사용하면 단순히 DB를 바꾸는 걸로 작업이 끝나지 않고 난수 생성에 대한 고민을 하셔야 할텐데 이에 대한 역할을 DB가 아닌 곳으로 넘겨보죠.
이건 아래 Request관련 내용에 남길게요!
@@ -1,24 +1,43 @@
 package webserver.controller;
 
 import db.DataBase;
+import db.HttpSessions;
 import model.User;
+import org.checkerframework.checker.units.qual.C;
 import webserver.domain.*;
 
+import java.util.UUID;
+
 public class LoginController extends AbstractController {
     @Override
     public HttpResponse doPost(HttpRequest httpRequest) throws Exception {
         User findUser = DataBase.findUserById(httpRequest.getParameter("userId"));
-        if (findUser == null || !findUser.isValidPassword(httpRequest.getParameter("password"))) {
+        if (wrongUser(httpRequest, findUser)) {
+            Cookie cookie = new Cookie("logined=false");
+            cookie.setPath("/");
             return new HttpResponse.Builder()
                     .status(HttpStatusCode.FOUND)
                     .redirect("/user/login_failed.html")
-                    .cookie(new Cookie("logined", "false", "Path", "/"))
+                    .cookie(cookie)
                     .build();
         }
+        Cookie cookie = new Cookie("logined=true");
+        cookie.setPath("/");
+        Session session = new Session();
+        session.addAttribute("user", findUser);
+        String uuid = HttpSessions.getId();
+        HttpSessions.addSession(uuid, session);
+        Cookie sessionCookie = new Cookie("Session=" + uuid);
+        sessionCookie.setPath("/");
         return new HttpResponse.Builder()
                 .status(HttpStatusCode.FOUND)
                 .redirect("/index.html")
-                .cookie(new Cookie("logined", "true", "Path", "/"))
+                .cookie(cookie)
+                .cookie(sessionCookie)
                 .build();
     }
+
+    private boolean wrongUser(HttpRequest httpRequest, User findUser) {
+        return findUser == null || !findUser.isValidPassword(httpRequest.getParameter("password"));
+    }
 }

리뷰: Session에 대한 추가로 상당히 복잡해졌네요. 
Controller의 대부분의 코드가 Http에 대한 작업인데 Session도 결국 Http의 스펙인걸 고려해서 Request, Response를 통해 관리할 수 있지 않을까요? (위에 말씀드린 session id의 추가 또한 말이죠.)

그러면 Controller는 Session을 가져오고, 추가 요청만 하는 방법으로 사용하기만 할 수 있을 것 같아요.
@@ -1,21 +1,27 @@
 package webserver.domain;
 
+
+import java.util.Objects;
+
 public class Cookie {
+    public static final String PATH = "Path";
     private String name;
     private String value;
-    private String pathName;
-    private String pathValue;
+    private String path;
 
     public Cookie(String name, String value) {
         this.name = name;
         this.value = value;
     }
 
-    public Cookie(String name, String value, String pathName, String pathValue) {
-        this.name = name;
-        this.value = value;
-        this.pathName = pathName;
-        this.pathValue = pathValue;
+    public Cookie(String cookieString) {
+        String[] splitData = cookieString.split("=");
+        this.name = splitData[0];
+        this.value = splitData[1];
+    }
+
+    public void setPath(String path) {
+        this.path = path;
     }
 
     public String getName() {
@@ -26,16 +32,33 @@ public String getValue() {
         return value;
     }
 
-    public String getPathName() {
-        return pathName;
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        Cookie cookie = (Cookie) o;
+        return Objects.equals(name, cookie.name) && Objects.equals(value, cookie.value);
     }
 
-    public String getPathValue() {
-        return pathValue;
+    @Override
+    public int hashCode() {
+        return Objects.hash(name, value);
     }
 
     @Override
     public String toString() {
-        return name + "=" + value + "; " + pathName + "=" + pathValue;
+        StringBuilder sb = new StringBuilder();
+        sb.append(HttpHeader.SET_COOKIE)
+                .append(": ")
+                .append(name)
+                .append("=")
+                .append(value)
+                .append("; ");
+        if (path != null) {
+            sb.append(PATH)
+                    .append("=")
+                    .append(path);
+        }
+        return sb.toString();
     }
 }

리뷰: `cookieString` 타입을 변수명에 써줄 필요는 없습니다!