ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,72 @@
+package christmas.domain;
+
+import christmas.constant.Constant;
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class Benefit {
+
+    private final Map<Event, Integer> details;
+
+    public Benefit() {
+        this.details = new EnumMap<>(Event.class);
+        initialBenefitDetails();
+    }
+
+    public int calculateTotalDiscount() {
+        return details.values()
+                .stream()
+                .reduce(0, Integer::sum);
+    }
+
+    public void addDiscountIntoDetails(Event event, int discount) {
+        details.computeIfPresent(event, (key, value) -> value + discount);
+    }
+
+    public String detailsToString() {
+        StringBuilder sb = new StringBuilder(Constant.NOTHING + System.lineSeparator());
+        if (calculateTotalDiscount() != 0) {
+            sb.setLength(0);
+            appendDetails(sb);
+        }
+        return sb.toString();
+    }
+
+    public int calculatePresentationPrice() {
+        return details.get(Event.PRESENTATION);
+    }
+
+    public String presentationMenuToString() {
+        Event presentation = Event.PRESENTATION;
+        StringBuilder sb = new StringBuilder(Constant.NOTHING + System.lineSeparator());
+        int discount = details.get(presentation);
+        if (discount != 0) {
+            sb.setLength(0);
+            sb.append(presentation.getTarget());
+            sb.append(Constant.SPACE);
+            sb.append(String.format(Constant.MENU_UNIT, Math.abs(discount / Event.PRESENTATION.getDiscount())));
+            sb.append(System.lineSeparator());
+        }
+        return sb.toString();
+    }
+
+
+    private void appendDetails(StringBuilder benefitDetail) {
+        for (Entry<Event, Integer> entry : details.entrySet()) {
+            String eventMessage = entry.getKey().getMessage();
+            int discount = entry.getValue();
+            if (discount != 0) {
+                benefitDetail.append(eventMessage);
+                benefitDetail.append(Constant.SPACE);
+                benefitDetail.append(String.format(Constant.PRICE_UNIT, discount));
+                benefitDetail.append(System.lineSeparator());
+            }
+        }
+    }
+
+    private void initialBenefitDetails() {
+        Event.getAllEvent().forEach(event -> details.put(event, 0));
+    }
+
+}

리뷰: 총혜택값이 0인지 아닌지를 분기점으로 둬서 출력 여부를 결정케 하네요! 수달님 로직을 보니까 굳이 각자 따로따로 생각할 필요가 없었을 것 같네요. 좋은 아이디어 같습니다:)
@@ -0,0 +1,82 @@
+package study;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class Equation {
+
+	private final String equation;
+
+	private static final String INVALID_CALCULATION_FORMAT = "잘못된 계산식입니다.";
+	private static final String DELIMITER = " ";
+
+	public Equation(String equation) {
+		this.equation = equation;
+		checkEquation();
+	}
+
+	public void checkEquation() {
+		String[] split = equation.split(DELIMITER);
+
+		for (int i = 0; i < split.length; ) {
+			checkDoubleNumber(split, i);
+			i += 2;
+		}
+
+		for (int i = 1; i < split.length; ) {
+			checkDoubleSymbol(split, i);
+			i += 2;
+		}
+
+	}
+
+	public List<Integer> getNumbers() {
+		List<Integer> numList = new ArrayList<>();
+		String[] inputArr = equation.split(DELIMITER);
+		Arrays.stream(inputArr).filter(this::isParesInt).forEach(s -> addNumList(numList, s));
+		return numList;
+	}
+
+	public List<String> getSymbolsList() {
+		List<String> symbolList = new ArrayList<>();
+		String[] inputArr = equation.split(DELIMITER);
+		Arrays.stream(inputArr).filter(s -> !isParesInt(s))
+			.forEach(s -> addSymbolList(symbolList, s));
+		return symbolList;
+	}
+
+	private boolean isParesInt(String input) {
+		return input.chars().allMatch(Character::isDigit);
+	}
+
+	private void addNumList(List<Integer> numList, String input) {
+		numList.add(Integer.parseInt(input));
+	}
+
+	private void addSymbolList(List<String> symbolList, String input) {
+		checkSymbol(input);
+		symbolList.add(input);
+	}
+
+	private void checkSymbol(String input) {
+		if (!SymbolStatus.checkSymbol(input)) {
+			throw new IllegalStateException(INVALID_CALCULATION_FORMAT);
+		}
+	}
+
+	private void checkDoubleNumber(String[] split, int i) {
+		boolean parseInt = isParesInt(split[i]);
+		if (!parseInt) {
+			throw new IllegalStateException(INVALID_CALCULATION_FORMAT);
+		}
+	}
+
+	private void checkDoubleSymbol(String[] split, int i) {
+		boolean parse = isParesInt(split[i]);
+		if (parse) {
+			throw new IllegalStateException(INVALID_CALCULATION_FORMAT);
+		}
+	}
+
+}

리뷰: 클래스의 구현 컨벤션에 맞춰서 구현하게되면 일관된 코드를 작성할 수 있습니다 :)

```
class A {
    상수(static final) 또는 클래스 변수
    인스턴스 변수
    생성자
    팩토리 메소드
    메소드
    기본 메소드 (equals, hashCode, toString)
}
```
@@ -0,0 +1,16 @@
+package study;
+
+import java.util.Scanner;
+
+public class InputView {
+
+	private Scanner scanner;
+
+	public InputView(Scanner scanner) {
+		this.scanner = scanner;
+	}
+
+	public String readEquation() {
+		return scanner.nextLine();
+	}
+}

리뷰: InputView에서 생성자의 파라미터로 Scanner를 설정해주신 이유가 있을까요?

생성자를 통한 의존성 주입을 통해 얻게되는 장점이 없어보이는 것 같아요 🤔
@@ -0,0 +1,13 @@
+package study;
+
+public class ResultView {
+
+	public void initStart() {
+		System.out.println("계산식을 입력하세요.");
+	}
+
+	public void viewResult(SimpleCalculator simpleCalculator) {
+		int result = simpleCalculator.calEquation();
+		System.out.println("계산 결과 = " + result);
+	}
+}

리뷰: 계산식을 입력하라고 콘솔에 출력되는 내용은 InputView에 좀 더 적합해보이기도 하네요.

InputView와 ResultView를 구분하는 이유는 요청과 결과를 구분하기 위한 부분이기 때문에 ResultView에서만 출력을 해야하는 것은 아닙니다 :)
@@ -0,0 +1,13 @@
+package study;
+
+public class ResultView {
+
+	public void initStart() {
+		System.out.println("계산식을 입력하세요.");
+	}
+
+	public void viewResult(SimpleCalculator simpleCalculator) {
+		int result = simpleCalculator.calEquation();
+		System.out.println("계산 결과 = " + result);
+	}
+}

리뷰: 계산기 객체를 viewResult()의 인자로 넘기는 것 보다는 결과 값을 넘겨주는 것은 어떨까요?

계산기 객체와 View 객체를 분리하는 이유는 서로의 의존성을 끊어내고 변경의 영향을 최소화하기 위함입니다.

만약 계산기 객체의 calEquation() 메서드의 네이밍이 바뀌게 된다면 ResultView 에도 변경이 생기지 않을까요?

```suggestion
	public void viewResult(int result) {
		System.out.println("계산 결과 = " + result);
	}
```
@@ -0,0 +1,75 @@
+package study;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+
+public class SimpleCalculatorTest {
+
+	@Test
+	void plusTest() {
+		//given
+		String input = "10 + 5";
+		Equation equation = new Equation(input);
+		SimpleCalculator calculator = new SimpleCalculator(equation);
+
+		//when
+		int result = calculator.cal(
+			equation.getSymbolsList().get(0),
+			equation.getNumbers().get(0),
+			equation.getNumbers().get(1));
+
+		//then
+		assertThat(result).isEqualTo(15);
+	}
+
+	@Test
+	void minusTest() {
+		//given
+		String input = "10 - 5";
+		Equation equation = new Equation(input);
+		SimpleCalculator calculator = new SimpleCalculator(equation);
+
+		//when
+		int result = calculator.cal(
+			equation.getSymbolsList().get(0),
+			equation.getNumbers().get(0),
+			equation.getNumbers().get(1));
+
+		//then
+		assertThat(result).isEqualTo(5);
+	}
+
+	@Test
+	void multiplyTest() {
+		//given
+		String input = "10 * 5";
+		Equation equation = new Equation(input);
+		SimpleCalculator calculator = new SimpleCalculator(equation);
+		//when
+		int result = calculator.cal(
+			equation.getSymbolsList().get(0),
+			equation.getNumbers().get(0),
+			equation.getNumbers().get(1));
+
+		//then
+		assertThat(result).isEqualTo(50);
+	}
+
+	@Test
+	void divisionTest() {
+		//given
+		String input = "10 / 5";
+		Equation equation = new Equation(input);
+		SimpleCalculator calculator = new SimpleCalculator(equation);
+		//when
+		int result = calculator.cal(
+			equation.getSymbolsList().get(0),
+			equation.getNumbers().get(0),
+			equation.getNumbers().get(1));
+
+		//then
+		assertThat(result).isEqualTo(2);
+	}
+
+}

리뷰: 계산기 테스트에도 @DisplayName 어노테이션을 활용하면 더 가독성 있는 테스트 코드를 작성 할 수 있을 것 같습니다 :)
@@ -0,0 +1,82 @@
+package study;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class Equation {
+
+	private final String equation;
+
+	private static final String INVALID_CALCULATION_FORMAT = "잘못된 계산식입니다.";
+	private static final String DELIMITER = " ";
+
+	public Equation(String equation) {
+		this.equation = equation;
+		checkEquation();
+	}
+
+	public void checkEquation() {
+		String[] split = equation.split(DELIMITER);
+
+		for (int i = 0; i < split.length; ) {
+			checkDoubleNumber(split, i);
+			i += 2;
+		}
+
+		for (int i = 1; i < split.length; ) {
+			checkDoubleSymbol(split, i);
+			i += 2;
+		}
+
+	}
+
+	public List<Integer> getNumbers() {
+		List<Integer> numList = new ArrayList<>();
+		String[] inputArr = equation.split(DELIMITER);
+		Arrays.stream(inputArr).filter(this::isParesInt).forEach(s -> addNumList(numList, s));
+		return numList;
+	}
+
+	public List<String> getSymbolsList() {
+		List<String> symbolList = new ArrayList<>();
+		String[] inputArr = equation.split(DELIMITER);
+		Arrays.stream(inputArr).filter(s -> !isParesInt(s))
+			.forEach(s -> addSymbolList(symbolList, s));
+		return symbolList;
+	}
+
+	private boolean isParesInt(String input) {
+		return input.chars().allMatch(Character::isDigit);
+	}
+
+	private void addNumList(List<Integer> numList, String input) {
+		numList.add(Integer.parseInt(input));
+	}
+
+	private void addSymbolList(List<String> symbolList, String input) {
+		checkSymbol(input);
+		symbolList.add(input);
+	}
+
+	private void checkSymbol(String input) {
+		if (!SymbolStatus.checkSymbol(input)) {
+			throw new IllegalStateException(INVALID_CALCULATION_FORMAT);
+		}
+	}
+
+	private void checkDoubleNumber(String[] split, int i) {
+		boolean parseInt = isParesInt(split[i]);
+		if (!parseInt) {
+			throw new IllegalStateException(INVALID_CALCULATION_FORMAT);
+		}
+	}
+
+	private void checkDoubleSymbol(String[] split, int i) {
+		boolean parse = isParesInt(split[i]);
+		if (parse) {
+			throw new IllegalStateException(INVALID_CALCULATION_FORMAT);
+		}
+	}
+
+}

리뷰: 임의의 숫자를 사용하는 매직 넘버는 소스 코드를 읽기 어렵게 만드는데요 ! 

숫자 2는 어떤 의미일까요?
@@ -0,0 +1,20 @@
+package study;
+
+import java.util.Scanner;
+
+public class Main {
+
+	public static void main(String[] args) {
+		ResultView resultView = new ResultView();
+
+		resultView.initStart();
+		Scanner scanner = new Scanner(System.in);
+		InputView inputView = new InputView(scanner);
+
+		Equation equation = new Equation(inputView.readEquation());
+		SimpleCalculator simpleCalculator = new SimpleCalculator(equation);
+
+		resultView.viewResult(simpleCalculator);
+
+	}
+}

리뷰: 파일 마지막에 엔터(개행문자)를 넣어주세요 :)

이유는 리뷰를 진행할 때 깃허브에서 경고메시지를 지우고 혹시 모르는 파일 읽기 오류에 대비하기 위함입니다.

좀 더 알고싶으시면 참고 링크를 보셔도 재밌을 것 같네요 :)

Intellij 를 사용하실 경우엔Preferences -> Editor -> General -> Ensure line feed at file end on save 를 체크해주시면파일 저장 시 마지막에 개행문자를 자동으로 넣어줍니다!

https://minz.dev/19https://stackoverflow.com/questions/729692/why-should-text-files-end-with-a-newline
@@ -0,0 +1,52 @@
+package study;
+
+public class SimpleCalculator {
+
+	private final Equation equation;
+
+	private static final String NO_DIVIDE_BY_ZERO = "0으로 나눌 수 없습니다.";
+
+	public SimpleCalculator(Equation equation) {
+		this.equation = equation;
+	}
+
+	public int cal(String symbol, Integer num1, Integer num2) {
+		if (symbol.equals(SymbolStatus.PLUS.toString())) {
+			return num1 + num2;
+		}
+
+		if (symbol.equals(SymbolStatus.MINUS.toString())) {
+			return num1 - num2;
+		}
+
+		if (symbol.equals(SymbolStatus.MULTIPLY.toString())) {
+			return num1 * num2;
+		}
+
+		if (symbol.equals(SymbolStatus.DIVISION.toString())) {
+			checkDivideByZero(num2);
+			return num1 / num2;
+		}
+
+		throw new IllegalStateException("잘못 입력하셨습니다..");
+	}
+
+	private void checkDivideByZero(Integer num) {
+		if (num == 0) {
+			throw new ArithmeticException(NO_DIVIDE_BY_ZERO);
+		}
+	}
+
+	public int calEquation() {
+		int cal = cal(
+			equation.getSymbolsList().get(0),
+			equation.getNumbers().get(0),
+			equation.getNumbers().get(1));
+
+		for (int i = 1; i < equation.getSymbolsList().size(); i++) {
+			cal = cal(equation.getSymbolsList().get(i), cal, equation.getNumbers().get(i + 1));
+		}
+
+		return cal;
+	}
+}

리뷰: `계산기`가 하나의 `방정식`을 상태로 가지도록 구현하셨네요 !
계산기가 또 다른 방정식에 대한 값은 어떻게 구할 수 있을까요?

현재 상태에서는 하나의 계산기 객체가 하나의 방정식만을 계산할 수 있는데요. 여러 개의 방정식에 대해 계산할 수 있도록 개선해보면 좋을 것 같습니다 :)
@@ -0,0 +1,52 @@
+package study;
+
+public class SimpleCalculator {
+
+	private final Equation equation;
+
+	private static final String NO_DIVIDE_BY_ZERO = "0으로 나눌 수 없습니다.";
+
+	public SimpleCalculator(Equation equation) {
+		this.equation = equation;
+	}
+
+	public int cal(String symbol, Integer num1, Integer num2) {
+		if (symbol.equals(SymbolStatus.PLUS.toString())) {
+			return num1 + num2;
+		}
+
+		if (symbol.equals(SymbolStatus.MINUS.toString())) {
+			return num1 - num2;
+		}
+
+		if (symbol.equals(SymbolStatus.MULTIPLY.toString())) {
+			return num1 * num2;
+		}
+
+		if (symbol.equals(SymbolStatus.DIVISION.toString())) {
+			checkDivideByZero(num2);
+			return num1 / num2;
+		}
+
+		throw new IllegalStateException("잘못 입력하셨습니다..");
+	}
+
+	private void checkDivideByZero(Integer num) {
+		if (num == 0) {
+			throw new ArithmeticException(NO_DIVIDE_BY_ZERO);
+		}
+	}
+
+	public int calEquation() {
+		int cal = cal(
+			equation.getSymbolsList().get(0),
+			equation.getNumbers().get(0),
+			equation.getNumbers().get(1));
+
+		for (int i = 1; i < equation.getSymbolsList().size(); i++) {
+			cal = cal(equation.getSymbolsList().get(i), cal, equation.getNumbers().get(i + 1));
+		}
+
+		return cal;
+	}
+}

리뷰: 메서드나 변수명에 약어를 사용하는 것은 지양해주세요.

약어는 별도의 규약으로 정해지지 않으면 소스코드의 가독성을 어렵게 만듭니다 :)

```suggestion
	public int calculate(String symbol, Integer num1, Integer num2) {
```
@@ -0,0 +1,52 @@
+package study;
+
+public class SimpleCalculator {
+
+	private final Equation equation;
+
+	private static final String NO_DIVIDE_BY_ZERO = "0으로 나눌 수 없습니다.";
+
+	public SimpleCalculator(Equation equation) {
+		this.equation = equation;
+	}
+
+	public int cal(String symbol, Integer num1, Integer num2) {
+		if (symbol.equals(SymbolStatus.PLUS.toString())) {
+			return num1 + num2;
+		}
+
+		if (symbol.equals(SymbolStatus.MINUS.toString())) {
+			return num1 - num2;
+		}
+
+		if (symbol.equals(SymbolStatus.MULTIPLY.toString())) {
+			return num1 * num2;
+		}
+
+		if (symbol.equals(SymbolStatus.DIVISION.toString())) {
+			checkDivideByZero(num2);
+			return num1 / num2;
+		}
+
+		throw new IllegalStateException("잘못 입력하셨습니다..");
+	}
+
+	private void checkDivideByZero(Integer num) {
+		if (num == 0) {
+			throw new ArithmeticException(NO_DIVIDE_BY_ZERO);
+		}
+	}
+
+	public int calEquation() {
+		int cal = cal(
+			equation.getSymbolsList().get(0),
+			equation.getNumbers().get(0),
+			equation.getNumbers().get(1));
+
+		for (int i = 1; i < equation.getSymbolsList().size(); i++) {
+			cal = cal(equation.getSymbolsList().get(i), cal, equation.getNumbers().get(i + 1));
+		}
+
+		return cal;
+	}
+}

리뷰: SymbolStatus enum 클래스를 만들어주셨네요 👍 

SymbolStatus를 통해 연산기호의 `상태`를 한 곳에서 관리하게 해주셨으니 계산을 하는 `행위`도 함께 enum에 구현해보면 어떨까요?

https://techblog.woowahan.com/2527/
@@ -0,0 +1,75 @@
+import pandas as pd
+import numpy as np
+
+#제출자 리스트(수기 입력)
+videos = ['조용원', '정태호', '정우민', '장진희', '임채은', '이승수', '이남준', '윤다솜', '오유리나', '신수아', '백송하', '박원빈', '김현우', '김현수', '김태호', '김태연', '김의식', '김영우', '김세형', '강지호', '강수현']
+#Urclass 수강생 현황 csv 불러오기
+df = pd.read_csv('1012.csv')
+
+def group_maker(random_seed, video=len(videos), videos=videos, ur_list=df, group_num=7):
+    """
+    인원 배치는 랜덤이되 각 조에 제출자의 수가 일정하게 들어가게끔 프로젝트 조 짜는 함수입니다
+
+    --파라미터--
+    * Random seed
+    * video = 실제 제출자 수 (int) -> 제출자 명단이 잘 입력됐나 확인하기 위함
+    * videos = 제출자 명단 (list)
+    * ur_list = Urclass에서 export한 기수 명단 (dataframe)
+    * group_num = 조 수 (int)
+    """
+    try:
+        assert len(videos) == video #제출자 명단이 잘 입력되었는지 확인
+
+        #미제출자 리스트
+        non_videos = [name for name in ur_list['이름'].values if name not in videos]
+
+        try: #미제출자 리스트가 잘 뽑혔는지 확인 (한글이라 잘못 뽑히는 경우 있음)
+            assert len(videos) + len(non_videos) == len(ur_list)
+
+            #조 배정 시작!
+            np.random.seed(random_seed)
+            np.random.shuffle(videos)
+            np.random.shuffle(non_videos)
+
+            video = int(len(videos)/group_num) #한 조 최소 제출자 인원
+            non_video = int(len(non_videos)/group_num) #한 조 최소 미제출자 인원
+            groups = {}
+
+            for i in range(group_num+1):
+                if i == group_num:
+                    #1조부터 미제출자 나머지 인원 배치
+                    for j in range(len(non_videos[non_video*i:])):
+                        groups[f'{j+1}조'].append(non_videos.pop())
+                    
+                    #뒷 조부터 제출자 나머지 인원 배치
+                    for j in range(len(videos[video*i:])):
+                        groups[f'{group_num-j}조'].append(videos.pop())
+
+                    return groups
+                
+                group = videos[video*i:video*(i+1)]
+                group = group + non_videos[non_video*i:non_video*(i+1)]
+                groups[f'{i+1}조'] = group
+        
+        except:
+            print(f'미 제출자 리스트를 확인하세요')
+            print(f'\t* 총 인원 수: {len(ur_list)}명, \n\t* 미 제출자 리스트: {len(non_videos)}명, \n{non_videos}, \n\t* 제출자 리스트: {len(videos)}명, {videos}')
+    
+    except:
+        print('입력한 제출자 인원 수와 제출자 명단에 있는 수가 일치하지 않습니다. 제출자 명단을 확인해 주세요')
+
+if __name__ == "__main__":
+    #그룹 만들기
+    groups = group_maker(random_seed=1012, video=21)
+
+    #명단 출력
+    for group in groups:
+        print(groups.get(group))
+    
+    #수강생 이름이 빠짐없이 들어갔는지 재확인
+    j=0
+    for group in groups:
+        i = len(groups.get(group))
+        j += i
+
+    assert j == len(df)

리뷰: read_csv 를 이용해서 list 로 읽기 위함이라면, csv reader 를 사용하시는건 어떨까요?
`import pandas` 를 하려면 45MB 정도의 pandas 가 필요한대신 csv 를 사용하면 16KB 만에 해결할 수 있습니다!
@@ -0,0 +1,75 @@
+import pandas as pd
+import numpy as np
+
+#제출자 리스트(수기 입력)
+videos = ['조용원', '정태호', '정우민', '장진희', '임채은', '이승수', '이남준', '윤다솜', '오유리나', '신수아', '백송하', '박원빈', '김현우', '김현수', '김태호', '김태연', '김의식', '김영우', '김세형', '강지호', '강수현']
+#Urclass 수강생 현황 csv 불러오기
+df = pd.read_csv('1012.csv')
+
+def group_maker(random_seed, video=len(videos), videos=videos, ur_list=df, group_num=7):
+    """
+    인원 배치는 랜덤이되 각 조에 제출자의 수가 일정하게 들어가게끔 프로젝트 조 짜는 함수입니다
+
+    --파라미터--
+    * Random seed
+    * video = 실제 제출자 수 (int) -> 제출자 명단이 잘 입력됐나 확인하기 위함
+    * videos = 제출자 명단 (list)
+    * ur_list = Urclass에서 export한 기수 명단 (dataframe)
+    * group_num = 조 수 (int)
+    """
+    try:
+        assert len(videos) == video #제출자 명단이 잘 입력되었는지 확인
+
+        #미제출자 리스트
+        non_videos = [name for name in ur_list['이름'].values if name not in videos]
+
+        try: #미제출자 리스트가 잘 뽑혔는지 확인 (한글이라 잘못 뽑히는 경우 있음)
+            assert len(videos) + len(non_videos) == len(ur_list)
+
+            #조 배정 시작!
+            np.random.seed(random_seed)
+            np.random.shuffle(videos)
+            np.random.shuffle(non_videos)
+
+            video = int(len(videos)/group_num) #한 조 최소 제출자 인원
+            non_video = int(len(non_videos)/group_num) #한 조 최소 미제출자 인원
+            groups = {}
+
+            for i in range(group_num+1):
+                if i == group_num:
+                    #1조부터 미제출자 나머지 인원 배치
+                    for j in range(len(non_videos[non_video*i:])):
+                        groups[f'{j+1}조'].append(non_videos.pop())
+                    
+                    #뒷 조부터 제출자 나머지 인원 배치
+                    for j in range(len(videos[video*i:])):
+                        groups[f'{group_num-j}조'].append(videos.pop())
+
+                    return groups
+                
+                group = videos[video*i:video*(i+1)]
+                group = group + non_videos[non_video*i:non_video*(i+1)]
+                groups[f'{i+1}조'] = group
+        
+        except:
+            print(f'미 제출자 리스트를 확인하세요')
+            print(f'\t* 총 인원 수: {len(ur_list)}명, \n\t* 미 제출자 리스트: {len(non_videos)}명, \n{non_videos}, \n\t* 제출자 리스트: {len(videos)}명, {videos}')
+    
+    except:
+        print('입력한 제출자 인원 수와 제출자 명단에 있는 수가 일치하지 않습니다. 제출자 명단을 확인해 주세요')
+
+if __name__ == "__main__":
+    #그룹 만들기
+    groups = group_maker(random_seed=1012, video=21)
+
+    #명단 출력
+    for group in groups:
+        print(groups.get(group))
+    
+    #수강생 이름이 빠짐없이 들어갔는지 재확인
+    j=0
+    for group in groups:
+        i = len(groups.get(group))
+        j += i
+
+    assert j == len(df)

리뷰: numpy 도 23 MB 입니다. python 에 기본으로 들어가있는 random 모듈을 사용하시는건 어떨까요?
@@ -0,0 +1,75 @@
+import pandas as pd
+import numpy as np
+
+#제출자 리스트(수기 입력)
+videos = ['조용원', '정태호', '정우민', '장진희', '임채은', '이승수', '이남준', '윤다솜', '오유리나', '신수아', '백송하', '박원빈', '김현우', '김현수', '김태호', '김태연', '김의식', '김영우', '김세형', '강지호', '강수현']
+#Urclass 수강생 현황 csv 불러오기
+df = pd.read_csv('1012.csv')
+
+def group_maker(random_seed, video=len(videos), videos=videos, ur_list=df, group_num=7):
+    """
+    인원 배치는 랜덤이되 각 조에 제출자의 수가 일정하게 들어가게끔 프로젝트 조 짜는 함수입니다
+
+    --파라미터--
+    * Random seed
+    * video = 실제 제출자 수 (int) -> 제출자 명단이 잘 입력됐나 확인하기 위함
+    * videos = 제출자 명단 (list)
+    * ur_list = Urclass에서 export한 기수 명단 (dataframe)
+    * group_num = 조 수 (int)
+    """
+    try:
+        assert len(videos) == video #제출자 명단이 잘 입력되었는지 확인
+
+        #미제출자 리스트
+        non_videos = [name for name in ur_list['이름'].values if name not in videos]
+
+        try: #미제출자 리스트가 잘 뽑혔는지 확인 (한글이라 잘못 뽑히는 경우 있음)
+            assert len(videos) + len(non_videos) == len(ur_list)
+
+            #조 배정 시작!
+            np.random.seed(random_seed)
+            np.random.shuffle(videos)
+            np.random.shuffle(non_videos)
+
+            video = int(len(videos)/group_num) #한 조 최소 제출자 인원
+            non_video = int(len(non_videos)/group_num) #한 조 최소 미제출자 인원
+            groups = {}
+
+            for i in range(group_num+1):
+                if i == group_num:
+                    #1조부터 미제출자 나머지 인원 배치
+                    for j in range(len(non_videos[non_video*i:])):
+                        groups[f'{j+1}조'].append(non_videos.pop())
+                    
+                    #뒷 조부터 제출자 나머지 인원 배치
+                    for j in range(len(videos[video*i:])):
+                        groups[f'{group_num-j}조'].append(videos.pop())
+
+                    return groups
+                
+                group = videos[video*i:video*(i+1)]
+                group = group + non_videos[non_video*i:non_video*(i+1)]
+                groups[f'{i+1}조'] = group
+        
+        except:
+            print(f'미 제출자 리스트를 확인하세요')
+            print(f'\t* 총 인원 수: {len(ur_list)}명, \n\t* 미 제출자 리스트: {len(non_videos)}명, \n{non_videos}, \n\t* 제출자 리스트: {len(videos)}명, {videos}')
+    
+    except:
+        print('입력한 제출자 인원 수와 제출자 명단에 있는 수가 일치하지 않습니다. 제출자 명단을 확인해 주세요')
+
+if __name__ == "__main__":
+    #그룹 만들기
+    groups = group_maker(random_seed=1012, video=21)
+
+    #명단 출력
+    for group in groups:
+        print(groups.get(group))
+    
+    #수강생 이름이 빠짐없이 들어갔는지 재확인
+    j=0
+    for group in groups:
+        i = len(groups.get(group))
+        j += i
+
+    assert j == len(df)

리뷰: csv 파일을 읽는 부분을 함수로 만들면 재사용하기 더 편리할 듯합니다.