본문 바로가기
Back-end/멋쟁이사자처럼

[멋쟁이사자처럼] 방학과제 2주차 피드백 정리

by 잔디🌿 2024. 3. 5.

    2주차 과제는 raceCar미션이었다.

    1주차와 마찬가지로 random을 사용하여 구현하였다.

    https://github.com/CNU-Likelion/12th-backend-racingcar/pull/1

     

    [202101947 김민지] 2주차 미션을 제출합니다. by alsswl · Pull Request #1 · CNU-Likelion/12th-backend-racingcar

    [202101947 김민지] 2주차 미션을 제출합니다.

    github.com

     

    <구현>

    위와 같은 순서로 구현하였다. 이전에 비해서 commit단위가 커진 것 같다....

     

    이번 과제에서 특이한 점은 getter과 setter을 사용하지 않는다는 조건이 붙은 것이다. 

    이제까지 다른 사람들의 코드를 보면 getter과 setter을 많이 사용했던 것 같고, 객체지향적으로 설계하려면 당연히 필요한 기능이라고 생각했어서 의아했다. 

    이에 대해 찾아본 결과, 객체 내부에 있는 데이터는 객체 스스로 처리하여 값을 넘겨주는 것이 좋은 코드라는 것을 알게되었다. 같은 프로젝트 안에서는 데이터를 나누는 것이 당연히 괜찮을 것이라고 생각했는데 아니었다. 당연하게 생각했던 것이 아닐수도 있다니 신기했다.

     

    <피드백 1 : 메소드명 camel case 지키기>

     

     메소드명은 첫 단어는 소문자, 이후부터 대문자로 써야한다. 분명 알고 있었지만 실수해버렸다. 다음부터는 제출 전에 한번 더 확인해야겠다.

     

    <피드백 2 : 클래스화가 필요하지 않은 기능들은 필요한 클래스에 static 메소드로 남긴다>

    원래는 위와 같이 랜덤인 수를 만드는 클래스를 만들었었다. 하지만 이 클래스는 단순한 유틸리티 메소드만 가지고 있기 때문에 클래스화가 필요하지 않는 것 같다고 하셨다.

    객체지향이 처음이라 어떤 단위로 클래스를 만들어야하는지 감이 잘 안잡힌다. 다른 사람들의 코드를 많이 참고해보아야겠다.

     

    <피드백 3 : 스택오버플로우를 방지하기 위해 재귀함수를 쓰지 않는다>

     

    구현해야하는 기능들 중에 입력을 잘못 받으면 에러를 발생시키고 새로 입력받는 부분이 있었다. 

    처음에 나는 이 부분을 재귀로 구현하였다.

    이렇게 재귀를 사용하면 코드는 간결하지만 스택오버플로우가 발생할 수 있다.

    이를 위와 같이 while문으로 변경하였다.

     

    <피드백 4 : 여러개의 요소를 가지고 있는 변수의 이름은 복수형으로 한다>

    위 코드에서 userName은 사용자의 이름들을 String배열에 모아 둔 변수이다.

    따라서 userName같은 단수형 이름을 사용하면 가독성이 떨어질 수 있다. 때문에 변수명을 userNames로 변경하였다.

     

    <피드백 5 : 클래스 내에서만 호출되는 메서드는 private로 선언한다>

     

    클래스내의 메서드를 거의 public으로 선언하다보니까 클래스 내에서만 사용되는 메서드도 public으로 선언하는 실수를 했다. 

    클래스 순서 정렬할 때 이런 함수들은 모두 private로 변경해야겠다.

     

     

    <피드백 6 : 한 클래스 내의 메서드는 클래스가 가진 책임만 수행하도록 한다>

     

    이 이론은 이미 알고 있었다. 하지만 이 개념은 내 머릿속에서 한 객체가 가지고 있는 상태는 그 안에서 처리해야한다는 개념과 충돌했다. 

    나는 후자를 선택하여 Car객체에 있는 position 상태를 숨기고자 각 car의 position을 나타내는 string을 만드는 메서드를 car클래스 내에 선언했다. 

    그런데 위 내용이 Car객체의 역할과는 맞지 않는다는 피드백을 받았다. 이에 대해서 질문한 결과 

    객체지향 관점에서는 Car객체 안에 있는 Name과 position은 변수로 선언하는 것이 아니라 데이터 운반 목적으로 사용되는 Name과 Position 클래스를 만들어서 각각의 객체로 다뤄야 한다는 답변을 받았다. 

     

    이를 반영하기 위해 name과 Position 객체를 각각 만들어서 해결했다. 

     

    위 개념을 이해하기 위해서 자바의 record 개념과 DTO를 알아보면 좋다고 하셨다.

     

    <record>

    불변 데이터 객체를 만들도록 해주는 자바의 기능이다.

    생성자를 작성하지 않아도 되고, toString, equals, hashCode 메소드에 대한 구현을 자동으로 제공한다.

    public record Car(String name, Integer position) { }

    위와 같은 코드를 풀어서 쓰면

    public class Car {
        private final String name;
        private final Int position;
    
        public MyRecord(String name, String position) {
            this.name = name;
            this.position = position;
        }
    
        public String getName() {
            return name;
        }
    
        public String getString() {
            return string;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Car car = (Car) o;
            return Objects.equals(name, car.name) && Objects.equals(position, car.position);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(name, position);
        }
    }

    이렇게 된다고 볼 수 있다.

     

    <DTO>

    DTO는 계층간 데이터 교환을 위해 사용하는 객체이다.

    DTO는 순수하게 데이터를 저장하고, 데이터에 대한 getter, setter 만을 가져야한다고 한다. DTO는 어떠한 비즈니스 로직을 가져서는 안되며, 저장, 검색, 직렬화, 역직렬화 로직만을 가져야 한다고 한다.

     

    DTO를 record로 구현하는 경우가 많다고 한다. 아직은 너무 어렵지만 다음에 한번 적용해보아야겠다.

     

    객체지향을 더 공부해서 멋사 본격적으로 시작할 때는 더욱 좋은 코드를 짤 수 있도록 해야겠다.