Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

πŸš€ 4단계 - μžλ™μ°¨ κ²½μ£Ό(우승자) #6036

Merged
merged 12 commits into from
Mar 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions src/main/java/racingcar/RacingCar.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,29 @@
import java.util.List;

public class RacingCar {

public static void main(String[] args) {
}
public static void gameStart() {
Cars cars = new Cars(getCars(InputView.inputValidatedNumberOfCar()));
Race race = new Race(cars, InputView.inputValidatedNumberOfAttempts());
race.start(createRandomStrategy());
}

public static ArrayList<Car> getCars(int car) {
public static void gameStartWithName() {
Race race = Race.create();
race.startWithName(createRandomStrategy());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

μ†Œμ†Œν•œ μ˜κ²¬μž…λ‹ˆλ‹€λ§Œ Raceλ‚˜ Carsκ°€ 정적 νŒ©ν† λ¦¬ λ©”μ†Œλ“œλ₯Ό μ œκ³΅ν•œλ‹€λ©΄
μ‚¬μš©ν•˜λŠ” 객체 μž…μž₯μ—μ„œ 훨씬 μˆ˜μ›”ν•  것 κ°™μŠ΅λ‹ˆλ‹€ πŸ˜„

이미 κ°μ²΄λ“€μ˜ μ •μ˜λŠ” 잘 ν•΄μ£Όμ…¨μœΌλ‹ˆ μ‚¬μš©μ„± κ°œμ„ μ„ μœ„ν•œ μΈν„°νŽ˜μ΄μŠ€μͺ½λ„
ν•œλ²ˆ μ •λ¦¬ν•˜κ³  κ°€μ‹œλ©΄ 쒋을 것 κ°™μŠ΅λ‹ˆλ‹€.
μš” λ‚΄μš©ν•œλ²ˆ λ³΄μ‹œλ©΄ λ„μ›€λ˜μ‹€ 것 κ°™μŠ΅λ‹ˆλ‹€ πŸ™

https://johngrib.github.io/wiki/pattern/static-factory-method/

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

정적 νŒ©ν† λ¦¬ λ©”μ†Œλ“œλŠ” 생각을 μ•ˆν•΄λ΄€λŠ”λ° 덕뢄에 배우고 κ°‘λ‹ˆλ‹€!
아직 μ–΄λ–»κ²Œ κ΅¬ν˜„ν•΄μ•Ό 쒋을지 감이 μ•ˆμ™€μ„œ 일단 Raceλ₯Ό μƒμ„±ν•˜λŠ” create와 Cars의 μƒμ„±μžλ₯Ό fromNames둜 κ΅¬ν˜„ν•΄λ΄€μŠ΅λ‹ˆλ‹€!

}


public static List<Car> getCars(int car) {
ArrayList<Car> cars = new ArrayList<>();
for(int i=0; i<car; i++){
for (int i = 0; i < car; i++) {
cars.add(new Car());
}
return cars;
}


private static RandomStrategy createRandomStrategy() {
return new RandomStrategy(new RandomGenerator());
}
Expand Down
24 changes: 22 additions & 2 deletions src/main/java/racingcar/domain/Car.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,41 @@
import racingcar.strategy.MovingStrategy;

public class Car {
public static final int CAR_NAME_SIZE = 5;

private String name;
private int moveCount;

public Car() {
this.moveCount = 1;
}

public Car(String name) {
validName(name);
this.name = name;
this.moveCount = 1;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

μ΄λ ‡κ²Œ 될 경우 Carλ₯Ό 직접 μƒμ„±ν•œλ‹€λ©΄ μ€‘μš”ν•œ 검증이 λˆ„λ½ 될 수 μžˆμŠ΅λ‹ˆλ‹€.
Carsμ—μ„œ 검증을 ν•˜κ³  κ³„μ‹œκΈ΄ ν•©λ‹ˆλ‹€λ§Œ, Carsλ₯Ό ν†΅ν•΄μ„œ Carλ₯Ό μƒμ„±ν•˜μ§€ μ•Šκ³ 
λ°”λ‘œ 생성할 수 도 있기 λ•Œλ¬Έμ— λˆ„λ½μ„ ν”Όν•˜λ €λ©΄ name을 가진 주체 도메인인
Carμ—μ„œ 검증을 ν•˜λŠ”κ²Œ 더 μžμ—°μŠ€λŸ¬μšΈ 것 κ°™μŠ΅λ‹ˆλ‹€ πŸ˜„

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

λ„΅! nameμ—λŒ€ν•œ 검증을 car μ—μ„œ ν•˜λ„λ‘ μˆ˜μ •ν•˜μ˜€μŠ΅λ‹ˆλ‹€! ( κΈ°μ‘΄ inpuview에 있던 검증 λ‘œμ§λ„ μ΄λ™ν•˜μ˜€μŠ΅λ‹ˆλ‹€. )


public String getName() {
return name;
}

public int getMoveCount() {
return moveCount;
}


public void move(MovingStrategy movingStrategy) {
if(movingStrategy.isMove()){
if (movingStrategy.isMove()) {
moveCount++;
}
}

private void validName(String name) {
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("μžλ™μ°¨ 이름은 λΉˆκ°’μΌ 수 μ—†μŠ΅λ‹ˆλ‹€.");
}
if (name.length() > CAR_NAME_SIZE) {
throw new IllegalArgumentException("μžλ™μ°¨ 이름은 5자λ₯Ό μ΄ˆκ³Όν•  수 μ—†μŠ΅λ‹ˆλ‹€.");
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’― πŸ’― πŸ’― πŸ’― πŸ’―

}
28 changes: 28 additions & 0 deletions src/main/java/racingcar/domain/Cars.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
import racingcar.ui.ResultView;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

public class Cars {

Expand All @@ -15,11 +17,37 @@ public Cars(List<Car> cars) {
this.cars = new ArrayList<>(cars);
}

public static Cars fromNames(String[] names) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

μ’‹λ„€μš” πŸ‘

validCarNames(names);
List<Car> carList = Arrays.stream(names)
.map(Car::new)
.collect(Collectors.toList());
return new Cars(carList);
}

private static void validCarNames(String[] names) {
if (Arrays.stream(names).distinct().count() < names.length) {
throw new IllegalArgumentException("μžλ™μ°¨ 이름은 쀑볡될 수 μ—†μŠ΅λ‹ˆλ‹€.");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

κ³ λ―Όν•˜μ‹  뢀뢄인 것 같은데 저도 이 μžλ¦¬κ°€ λ§žλ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€ πŸ˜„
μ†Œμ†Œν•œ μ˜κ²¬μž…λ‹ˆλ‹€λ§Œ Set을 ν™œμš©ν•΄λ³΄μ‹œλŠ”κ²ƒλ„ 방법쀑에 ν•˜λ‚˜ μž…λ‹ˆλ‹€ πŸ™

}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

μœ„μ—μ„œλ„ μ˜κ²¬λ“œλ Έμ§€λ§Œ name을 κ΄€λ¦¬ν•˜λŠ” κ°μ²΄μ—μ„œ κ²€μ¦μ±…μž„κΉŒμ§€ κ°€μ Έκ°€λŠ”κ²Œ 더 μžμ—°μŠ€λŸ¬μšΈ 것 κ°™μŠ΅λ‹ˆλ‹€ πŸ˜„
μΆ”κ°€λ‘œ 이 뢀뢄에 λŒ€ν•œ 검증이 ν•„μš”ν•  것 κ°™μ•„μš” πŸ™ (μ€‘λ³΅μž…λ ₯에 κ΄€ν•œ λΆ€λΆ„)
image

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

μ€‘λ³΅μ—λŒ€ν•œ 검증은 μ–΄λ””μ„œ ν•˜λ©΄ 쒋을지 고민을 ν•΄λ΄€λŠ”λ° Carsμ—μ„œ κ²€μ¦ν•˜λ„λ‘ μΆ”κ°€λ₯Ό ν–ˆμŠ΅λ‹ˆλ‹€!
이미 Carμ—μ„œ nameμ—λŒ€ν•΄ 검증을 ν•˜κΈ° λ•Œλ¬Έμ— namelistλ₯Ό λ”°λ‘œ μΆ”κ°€ν•΄μ„œ 쀑볡 검증을 할지,
Cars μ—μ„œ name listλ₯Ό λ°›μ•„ Car 객체λ₯Ό κ΅¬ν˜„ν•˜λŠ” 뢀뢄이 있기 λ•Œλ¬Έμ— ν•΄λ‹Ή λΆ€λΆ„μ—μ„œ 검증을 할지,
Raceμ—μ„œ ν•΄λ‹Ή λΆ€λΆ„ 검증 λ‘œμ§μ„ 좔가할지 κ³ λ―Όν•˜λ‹€κ°€ ν˜„μž¬ μ œκ°€ κ΅¬ν˜„ν•œ μ½”λ“œμ—μ„œλŠ” Car객체λ₯Ό μƒμ„±ν•˜λŠ” 둜직이 Cars에 μžˆκΈ°λ•Œλ¬Έμ—
Cars에 쀑볡 검증 λ‘œμ§μ„ μΆ”κ°€ν•΄λ΄€μŠ΅λ‹ˆλ‹€! 검증 λ‘œμ§μ„ 어디에 κ΅¬ν˜„ν•˜λŠ”μ§€ 고민이 많이 λ˜λŠ” λΆ€λΆ„μ΄λ„€μš” πŸ˜‚


public void moveAll(MovingStrategy strategy) {
cars.forEach(car -> car.move(strategy));
}

public List<Car> getCurrentStatus() {
return Collections.unmodifiableList(cars);
}

public List<String> getWinners() {
int maxMoveCount = cars.stream()
.mapToInt(Car::getMoveCount)
.max()
.orElse(0);

return cars.stream()
.filter(car -> car.getMoveCount() == maxMoveCount)
.map(Car::getName)
.collect(Collectors.toList());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 본연에 더 μ§‘μ€‘λ˜μ—ˆλ„€μš” πŸ˜„

μ•„μ£Ό μ†Œμ†Œν•œ 의견이긴 ν•©λ‹ˆλ‹€λ§Œ CarsλŠ” μ΅œμ’…κ²°κ³Όλ§Œ μ €μž₯ν•˜κ³  μžˆλŠ” κ΅¬μ‘°μž…λ‹ˆλ‹€.
예λ₯Όλ“€μ–΄ μ΅œμ’… κ²½κΈ°λ₯Ό 끝내고 λ‚œ λ’€ 각 λΌμš΄λ“œ 별 1μœ„λ₯Ό κ΅¬ν•œλ‹€λ˜μ§€,
각 λΌμš΄λ“œ 별 λ­”κ°€ 정보λ₯Ό μ‘°νšŒν•˜λ €λ©΄ Carsλ§ŒμœΌλ‘œλŠ” μ’€ μ–΄λ €μšΈ 것 κ°™μŠ΅λ‹ˆλ‹€.
κ²½κΈ° κ²°κ³Όλ₯Ό λ”°λ‘œ μ €μž₯ν•˜κ³  μžˆλŠ” 객체가 μžˆλ‹€λ©΄ μœ„μ™€ 같은 변경사항에도 μ’€ 더
μœ μ—°ν•  것 κ°™μŠ΅λ‹ˆλ‹€.

κ°œμ„  사항 κΉŒμ§„ μ•„λ‹ˆκ³  ꡬ쑰적인 μ˜κ²¬μ΄λ‹ˆ μ°Έκ³  μ •λ„λ§Œ ν•΄μ£Όμ„Έμš” πŸ˜„
μ§€κΈˆ μš”κ΅¬μ‚¬ν•­μ—μ„œλŠ” 이정도 κ΅¬ν˜„λ„ μΆ©λΆ„ν•©λ‹ˆλ‹€ πŸ™

}
}
16 changes: 16 additions & 0 deletions src/main/java/racingcar/domain/Race.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package racingcar.domain;

import racingcar.strategy.MovingStrategy;
import racingcar.ui.InputView;
import racingcar.ui.ResultView;

public class Race {
Expand All @@ -12,6 +13,12 @@ public Race(Cars cars, int totalAttempts) {
this.totalAttempts = totalAttempts;
}

public static Race create() {
String[] carNames = InputView.inputdNameOfCar();
int attempts = InputView.inputValidatedNumberOfAttempts();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

attempts도 μ›μ‹œκ°μ²΄λ‘œ 포μž₯되면 μ–΄λ–¨κΉŒμš” ?? πŸ˜„
이 ν›„ λ―Έμ…˜μ—μ„œ 많이 μ—°μŠ΅ν•˜μ‹œκ²Œ 될 텐데 ν•œλ²ˆ κ²€ν† ν•΄μ£Όμ‹œλ©΄ 쒋을 것 κ°™μŠ΅λ‹ˆλ‹€ πŸ™

μš” λ‚΄μš© ν•œλ²ˆ 보고 κ°€μ‹œλ©΄ 쒋을 것 κ°™μŠ΅λ‹ˆλ‹€ πŸ™‡

https://velog.io/@kanamycine/Java-%EC%9B%90%EC%8B%9C%EA%B0%92-%ED%8F%AC%EC%9E%A5

return new Race(Cars.fromNames(carNames), attempts);
}

public void start(MovingStrategy strategy) {
System.out.println("μ‹€ν–‰ κ²°κ³Ό");
ResultView.viewRacingCar(cars.getCurrentStatus());
Expand All @@ -20,4 +27,13 @@ public void start(MovingStrategy strategy) {
ResultView.viewRacingCar(cars.getCurrentStatus());
}
}
public void startWithName(MovingStrategy strategy) {
System.out.println("μ‹€ν–‰ κ²°κ³Ό");
ResultView.viewRacingCarWithName(cars.getCurrentStatus());
for (int attempt = 1; attempt < totalAttempts; attempt++) {
cars.moveAll(strategy);
ResultView.viewRacingCarWithName(cars.getCurrentStatus());
}
ResultView.viewRacingCarWinner(cars.getWinners());
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

μš”κ΅¬μ‚¬ν•­ κ΅¬ν˜„ πŸ‘

}
1 change: 1 addition & 0 deletions src/main/java/racingcar/strategy/MovingStrategy.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package racingcar.strategy;

@FunctionalInterface
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

μš”μ²­μ‚¬ν•­ 반영 πŸ‘

public interface MovingStrategy {
boolean isMove();
}
11 changes: 10 additions & 1 deletion src/main/java/racingcar/ui/InputView.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

public class InputView {

public static final String CAR_NAME_DELIMITER = ",";
public static Scanner SCANNER = new Scanner(System.in);
public static final int CAR_MIN = 0;
public static final int NUM_MIN = 0;
Expand All @@ -21,7 +22,10 @@ public static int inputNumberOfAttempts() {
System.out.println("μ‹œλ„ν•  νšŒμˆ˜λŠ” λͺ‡ 회 μΈκ°€μš”?");
return SCANNER.nextInt();
}

public static String inputNameOfCar() {
System.out.println("κ²½μ£Όν•  μžλ™μ°¨ 이름을 μž…λ ₯ν•˜μ„Έμš”(이름은 μ‰Όν‘œ(,)λ₯Ό κΈ°μ€€μœΌλ‘œ ꡬ뢄).");
return SCANNER.nextLine();
}
public static int inputValidatedNumberOfCar() {
int input = inputNumberOfCar();
validateInput(input, "μ°¨λŸ‰ 수", CAR_MIN);
Expand All @@ -32,6 +36,11 @@ public static int inputValidatedNumberOfAttempts() {
validateInput(input, "μ‹œλ„ 횟수", NUM_MIN);
return input;
}
public static String[] inputdNameOfCar() {
String input = inputNameOfCar();
input.split(CAR_NAME_DELIMITER);
return input.split(",");
}

private static void validateInput(int value, String fieldName, int min) {
if (value <= min) {
Expand Down
21 changes: 19 additions & 2 deletions src/main/java/racingcar/ui/ResultView.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,36 @@
package racingcar.ui;

import racingcar.domain.Car;
import racingcar.domain.Cars;

import java.util.List;
import java.util.stream.Collectors;

public class ResultView {

public static void viewRacingCar(List<Car> cars) {
for(Car car : cars) {
for(int i = 0; i < car.getMoveCount(); i++){
for (Car car : cars) {
for (int i = 0; i < car.getMoveCount(); i++) {
System.out.print("-");
}
System.out.println();
}
System.out.println();

}

public static void viewRacingCarWithName(List<Car> cars) {
for (Car car : cars) {
System.out.print(car.getName() + " : ");
for (int i = 0; i < car.getMoveCount(); i++) {
System.out.print("-");
}
System.out.println();
}
System.out.println();
}

public static void viewRacingCarWinner(List<String> winners) {
System.out.println(String.join(",", winners) + "κ°€ μ΅œμ’… μš°μŠΉν–ˆμŠ΅λ‹ˆλ‹€.");
}
}
79 changes: 65 additions & 14 deletions src/test/java/racingcar/RacingCarTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import racingcar.ui.InputView;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import static org.assertj.core.api.Assertions.assertThat;
Expand All @@ -30,34 +30,34 @@ void setUp() {

@Test
@DisplayName("전진 쑰건에 λ§Œμ‘±ν•˜λ©΄ 전진")
void move(){
car.move(()->true);
void move() {
car.move(() -> true);
assertThat(car.getMoveCount()).isEqualTo(2);
}

@Test
@DisplayName("전진 쑰건에 λ§Œμ‘±ν•˜μ§€ μ•ŠμœΌλ©΄ 멈좀")
void stop(){
car.move(()->false);
void stop() {
car.move(() -> false);
assertThat(car.getMoveCount()).isEqualTo(1);
}

@Test
@DisplayName("전진 쑰건에 λ§Œμ‘±ν•˜λ©΄ λͺ¨λ‘ 전진")
void moveAll(){
cars.moveAll(()->true);
void moveAll() {
cars.moveAll(() -> true);

assertAll(
() -> assertThat(cars.getCurrentStatus().get(0).getMoveCount()).isEqualTo(2),
() -> assertThat(cars.getCurrentStatus().get(1).getMoveCount()).isEqualTo(2),
() -> assertThat(cars.getCurrentStatus().get(2).getMoveCount()).isEqualTo(2)
);
);
}

@Test
@DisplayName("전진 쑰건에 λ§Œμ‘±ν•˜μ§€ μ•ŠμœΌλ©΄ λͺ¨λ‘ 멈좀")
void stopAll(){
cars.moveAll(()->false);
void stopAll() {
cars.moveAll(() -> false);

assertAll(
() -> assertThat(cars.getCurrentStatus().get(0).getMoveCount()).isEqualTo(1),
Expand All @@ -68,13 +68,13 @@ void stopAll(){

@Test
@DisplayName("random 값은 0κ³Ό 9사이 κ°’")
void random(){
void random() {
assertThat(RandomGenerator.generate()).isBetween(0, 9);
}

@Test
@DisplayName("μžλ™μ°¨ λŒ€μˆ˜κ°€ 0보닀 μž‘μ„ 경우 였λ₯˜ 리턴")
void inputCar(){
void inputCar() {
String input = "0";
InputView.setScanner(new Scanner(new ByteArrayInputStream(input.getBytes())));

Expand All @@ -84,7 +84,7 @@ void inputCar(){

@Test
@DisplayName("μ‹œλ„ν•  νšŒμˆ˜κ°€ 0보닀 μž‘μ„ 경우 였λ₯˜ 리턴")
void inputAttempts(){
void inputAttempts() {
String input = "0";
InputView.setScanner(new Scanner(new ByteArrayInputStream(input.getBytes())));

Expand All @@ -95,9 +95,60 @@ void inputAttempts(){
@Test
@DisplayName("λ°˜ν™˜λœ μžλ™μ°¨ List의 ν¬κΈ°λŠ” μž…λ ₯된 크기와 κ°™λ‹€.")
public void getCars() {
ArrayList<Car> cars = RacingCar.getCars(CAR);
List<Car> cars = RacingCar.getCars(CAR);
assertThat(cars.size()).isEqualTo(CAR);
}

@Test
@DisplayName("μžλ™μ°¨ 이름이 κ³΅λž€μΌ 경우 였λ₯˜ 리턴")
void inputCarName_empty() {
assertThatThrownBy(() -> new Car(""))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
@DisplayName("μžλ™μ°¨ 이름이 5자 μ΄ˆκ³Όν•  경우 였λ₯˜ 리턴")
void inputCarName_5자초과() {
assertThatThrownBy(() -> new Car("hyundai"))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
@DisplayName("μžλ™μ°¨ 이름이 쀑볡될 경우 였λ₯˜ 리턴")
void inputCarName_μ€‘λ³΅λœμ΄λ¦„() {
String[] names = {"pobi", "pobi"};
assertThatThrownBy(() -> Cars.fromNames(names))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
@DisplayName("μžλ™μ°¨ 이름은 μ‰Όν‘œλ‘œ ꡬ뢄")
void inputCarName_split() {
String input = "pobi,crong,honux";
InputView.setScanner(new Scanner(new ByteArrayInputStream(input.getBytes())));
assertThat(InputView.inputdNameOfCar()).containsExactly(input.split(","));

}

@Test
@DisplayName("단일 우승자 κ²°μ • ")
void getWinner() {
Car pobi = new Car("pobi");
Car crong = new Car("crong");
Cars cars = new Cars(List.of(pobi, crong));
pobi.move(() -> true);

assertThat(cars.getWinners()).containsExactly("pobi");
}

@Test
@DisplayName("곡동 우승자 κ²°μ • ")
void getWinners() {
Car pobi = new Car("pobi");
Car crong = new Car("crong");
Cars cars = new Cars(List.of(pobi, crong));

assertThat(cars.getWinners()).containsExactly("pobi", "crong");
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ν…ŒμŠ€νŠΈ μž‘μ„± μ’‹λ„€μš” πŸ‘


}
Loading