Good Code, Bad Code

견고성 : 오류를 처리하고 계속 진행
실패 : 더 높은 코드 계층이 오류를 처리하게 하거나 전체 프로그램 작동울 멈춘다

견고성보다는 실패가 많은 경우에서 유리하고 적절한 수준의 로직에서 견고성도 가질 수 있다.

failing fast : 가능한 한 문제의 실제 발생 지점으로부터 가까운 곳에서 오류를 나타내는 것.
실패가 신속하게 이뤄지면, 오류는 실제 위치 근처에서 나타나며 스택 트레이스는 해당 오류의 위치에 대한 정확한 코드 위치를 제공한다.
이렇게 발생하자마자 바로 실패나 오류를 보여주지 않으면 문제가 발생할 때 디버그하기 어려울 뿐만 아니라, 코드가 제대로 작동하지 않거나 잠재적으로 문제를 일으킬 수 있다.

failing loudly : 오류가 발생했다는 사실을 알아차리지 못하면 고칠 수 있는 방법이 없다. 따라서 오류가 발생하면 예외를 발생시켜 프로그램이 중단되게 한다.

모든 엔티티를 작게 유지

객체지향 생활 체조 - 규칙 6으로 50줄 이상 되는 클래스와 10개 파일 이상 되는 패키지는 없어야 한다는 뜻이다.
무조건 그래야 한다는 것은 아니지만 라인과 클래스 파일이 너무 많다면 분리를 생각해 볼 것.


블랙잭 미션,,, 차근차근해보자...

'회고 > 우아한테크코스' 카테고리의 다른 글

2023.03.08 일일 회고  (0) 2023.03.09
2023.03.07 일일 회고  (1) 2023.03.07
2023.02.28 일일 회고  (0) 2023.03.01
2023.02.27 일일 회고  (0) 2023.02.28
2023.02.23 일일 회고  (1) 2023.02.24

String

문자열을 생성하는 방법

final String a = "a";    // 리터럴
final String b = new String("a");    // new 연산자를 통한 객체 생성
final String c = new String("a").intern();

리터럴 방식으로 문자열을 생성하면 String Constant Pool에 문자열이 생성된다.
new 연산자를 통해 객체를 생성하면 Heap 영역에 문자열이 생성된다.
new 연산자를 통해 객체를 생성하고 intern()을 호출하면 Heap 영역에 문자열이 생성되었다가 String pool에 같은 값을 가지는 String이 있다면 해당 Reference를 반환한다. 즉 a == c 이다.

+ 연산자

String은 불변 객체이므로 + 연산자를 사용하면 새로운 객체를 계속 만들어낸다.
"a" + "b" + "c"를 실행하면 "ab" 객체가 만들어지고 "abc" 객체가 만들어진다.

JAVA9 부터는 String의 + 연산자의 내부 구현이 StringConcatFactory.makeConcatWithconstants() 를 사용하여
내부적으로 StringBuilder를 사용하여 문자열 결합을 수행하므로, 성능이 향상된다. 또한, 이 메소드는 컴파일 시점에 최적화된 코드를 생성하므로, 코드가 더 간결하고 가독성이 높아진다.

그렇다면 그냥 + 연산자를 막 사용해도 될까? 아래의 코드를 보자.

    private static void usePlus() {
        String a = "a";
        long start = System.currentTimeMillis();

        for (int i = 0; i < 30; i++) {
            a += a;
        }

        long end = System.currentTimeMillis();

        System.out.println("time : " + (end - start));
    }
time : 1446

위의 코드에서는 반복을 30번만 해도 시간이 꽤 오래걸린다.
StringConcatFactory.makeConcatWithconstants()는 내부적으로 StringBuilder를 생성하지만 위와 같은 상황에서
매 반복마다 StringBuilder를 새로 생성하기 때문에 성능이 저하된다.

    private static void useStringBuilder() {
        StringBuilder a = new StringBuilder("a");
        long start = System.currentTimeMillis();

        for (int i = 0; i < 30000; i++) {
            a.append("a");
        }

        long end = System.currentTimeMillis();

        System.out.println("time : " + (end - start));
    }
time : 4

이러한 상황에서는 위와 같이 StringBuilder를 하나만 생성하고 append를 하도록 처리해주자.

StringBuffer

StringBuffer는 멀티 스레드 환경에서 효율적이다.

StringBuilder

StringBuilder가 JDK1.5에서 추가되었다.
StringBuilder는 단일 스레드환경에서 뛰어난 성능을 보여준다.
실제로 문자열을 다룰 때 멀티 스레드환경에서 다룰 일이 없으므로 StringBuilder를 사용하는 것이 옳다.

일급 컬렉션의 객체 생성 책임

일급 컬렉션 객체를 생성할 때, 컬렉션 객체를 생성하는 책임을 일급 컬렉션 객체 내부에 두는 것이 좋다. 이렇게 하면 일급 컬렉션 객체의 구현 세부 사항을 외부로부터 감추어 줄 수 있다. 즉, 일급 컬렉션 객체를 사용하는 코드에서는 단순히 일급 컬렉션 객체를 생성하고 사용하기만 하면 된다.

반면에, 컬렉션 객체를 생성하는 책임을 외부로 분리하는 것은 일급 컬렉션 객체의 책임 범위를 벗어나는 것이다. 이렇게 하면 일급 컬렉션 객체의 구현 세부 사항을 외부에서 알아야 하며, 일급 컬렉션 객체를 사용하는 코드에서도 컬렉션 객체를 생성하는 코드를 함께 작성해야 한다. 이는 코드의 복잡도를 증가시킬 수 있으며, 유지보수성을 저해할 수 있다.

따라서, 일급 컬렉션 객체의 컬렉션 생성 책임은 일급 컬렉션 객체 자체가 갖는 것이 가장 적절하다. 이렇게 함으로써, 일급 컬렉션 객체의 책임 범위를 명확히 하고, 코드의 가독성과 유지보수성을 향상시킬 수 있다.

그러나 일급 컬렉션 객체가 생성하는 요소가 복잡한 객체인 경우, 해당 객체를 생성하는 책임은 일반적으로 일급 컬렉션 객체의 책임 범위를 벗어난다. 이 경우, 일급 컬렉션 객체는 복잡한 객체를 생성하기 위해 필요한 정보를 수집하고, 외부 팩토리(factory)나 빌더(builder) 패턴 등의 디자인 패턴을 활용하여 요소 객체를 생성할 수 있다.

따라서, 일급 컬렉션 객체가 생성하는 요소에 따라 책임 범위가 달라지므로, 일급 컬렉션 객체의 구현을 설계할 때에는 이러한 차이점을 고려하여야 한다.

'회고 > 우아한테크코스' 카테고리의 다른 글

2023.03.07 일일 회고  (1) 2023.03.07
2023.03.02 일일 회고  (0) 2023.03.03
2023.02.27 일일 회고  (0) 2023.02.28
2023.02.23 일일 회고  (1) 2023.02.24
2023.02.22 일일 회고  (0) 2023.02.23

사다리 생성 미션을 마무리하면서 사용했던 코드들을 정리해 보자.

원시값 포장

이번 미션에서 제일 많이 고민했던 부분이다. Player 클래스의 이름을 갖고 있는 String을 Name으로 포장하였다.
다음 두 코드를 보고 차이를 보자. 생성자에서 포장된 객체를 받는 것이 좋을까 원시 값을 받아서 생성자에서 포장해 주는 것이 좋을까?

public class Player {

    private Name name;    

    public Player(Name name) {
        this.name = name;
    }
}

생성자에서 포장된 객체를 받는 것은 도메인의 코드가 깔끔해 보인다.

public class Player {

    private Name name;    

    public Player(String name) {
        this.name = new Name(name);
    }
}

이는 도메인의 코드가 조금은 지저분해 보일 수 있다. 처음에는 무조건 "포장된 객체가 다른 계층으로 돌아다니는 것이 안전하다"라고 생각해서 포장된 객체를 전달받고, 전달했다. 어떤 방법이 맞는지 생각해 보기 위해 원시값 포장을 왜 하는지 생각해 보자.
원시값 포장은 객체의 책임을 분리하기 위해서 사용한다. Name에 관한 로직을 Name클래스에서 담당하도록 책임을 분리하는 것이다.
이러한 이유로 사용하는데 다른 계층에 원시값으로 돌아다녀도 상관이 없다! 다른 계층으로 돌아다닐 때는 Name의 값이 필요한 것이고 Name에 대한 로직을 사용할 때는 도메인 내에서 사용하기 때문에 원시값으로 돌아다녀도 상관이 없는 것 같다.

로직의 실행 위치

만약 Name을 검증하는 로직이 추가되어 Name의 생성자에서 validate() 메서드를 실행하고, 잘못된 값을 입력하면 예외를 던진다고 해보자. Name은 Player가 생성될 때 의미가 있다. 따라서 잘못된 Name에 대한 예외도 Player가 생성될 때 발생하는 것이 맞다. 따라서 생성자에서 포장을 하는 방식이 더 좋아 보인다.

테스트하기 좋은 코드

테스트의 관점에서 살펴보자.
test1은 포장된 객체를 전달받는 방식, test2는 원시값을 받아 생성자에서 포장하는 방식이다.

@Test
void test1() {
    Player player = new Player(new Name("maco"));
}

test1의 방식은 테스트를 할 때 Name객체를 직접 생성해서 넣어주어야 한다.

@Test
void test2() {
    Player player = new Player("maco");
}

test2의 방식은 이름값만 넣어주면 된다.
그래서 어떤 방법이 더 좋을까? test1은 Player를 테스트하는데 Name까지 알고 있어야 한다. 알아야 하는 정보가 더 많기 때문에 복잡하다고 할 수 있고 아는 다른 개발자들을 힘들게 할 것이다. 따라서 test2의 방식, 원시값을 전달받아 생성자에서 포장해 주는 방법이 나은 것 같다. 항상 이 방법이 맞다고 할 순 없을 것 같다. 상황에 따라 원시값 포장의 의미를 잘 생각하면서 상황에 맞게 사용하자!

'회고 > 우아한테크코스' 카테고리의 다른 글

2023.03.02 일일 회고  (0) 2023.03.03
2023.02.28 일일 회고  (0) 2023.03.01
2023.02.23 일일 회고  (1) 2023.02.24
2023.02.22 일일 회고  (0) 2023.02.23
2023.02.21 일일 회고  (0) 2023.02.22

원시값 포장 지옥

사다리 생성 미션 2단계를 시작하면서 새롭게 추가된 요구사항에 대한 기능 목록 작성부터 시작했다.
기능 목록을 작성하고 작성했던 코드들를 확인하니 디미터의 법칙을 위반한 코드들이 많이 보였다.
도메인에서 포장한 원시값을 반환할 때 포장을 풀어서 값을 넘겨주는 방식으로 해결했다.
디미터의 법칙은 해결했지만, 원시값이 도메인, 컨트롤러, 뷰를 넘나들면서 언제 포장을 해제해야하는가? 대한 고민을 하게 되었다.
처음에는 전부 포장된 상태로 넘겨서 뷰, 컨트롤러에서도 직접 포장된 값을 꺼내서 사용하도록 했다. 이러한 방식은 뷰와 컨트롤러가 도메인에 의존하는게 아닌가라는 생각이 들었다. 따라서 포장된 값은 도메인 내에서만 사용하고 컨트롤러와 뷰에 데이터가 왔다갔다 할 때는 포장을 풀어서 원시값을 넘겨주었다. 아직 소스를 수정하고 있지만 이 방법이 조금 더 깔끔해 보인다. 하지만 이 방법이 맞는걸까....
원시값 포장의 정의와 장점에 대해 다시 알아 보았다.

원시값 포장

원시 유형의 값(변수명)을 이용해 의미를 나타내지 않고, 의미있는 객체로 포장한다는 개념이다.

장점

  • 코드 가독성이 좋아진다. 포장 객체는 값의 의미를 명확하게 표현할 수 있다.
  • 유효성 검사(Validation) 등의 기능을 포함할 수 있다.
  • 포장 객체를 사용하면 이후에 값에 대한 수정이 필요할 때 모든 참조를 수정하지 않아도 되므로 유연성이 높아진다.

요약하면 값의 의미를 나타내고, 기능을 포함하고, 수정을 최소화해 유연성을 높인다.
값의 의미는 뷰와 컨트롤러에서도 나타내는 것이 좋을것 같다.
유효성 검사와 같은 기능은 도메인 뿐만 아니라 뷰에서도 사용할 수 있다.
수정의 최소화도 도메인과 뷰 모두 포함될 것 같다.

따라서, 포장과 해제를 도메인에서만 담당하는 것은 옳지 않을 수 있겠다. 컨트롤러에서 담당해보는것은 어떨까?
컨트롤러에서 포장과 해제를 담당하면 컨트롤러의 책임이 커질 수 있다. 포장과 해제만 담당하는 클래스로 분리하는 것은 어떤가...?
이 지옥에서 빠져나갈 수 있을까.... 내일 맑은 정신으로 다시 생각해보자.....

'회고 > 우아한테크코스' 카테고리의 다른 글

2023.02.28 일일 회고  (0) 2023.03.01
2023.02.27 일일 회고  (0) 2023.02.28
2023.02.22 일일 회고  (0) 2023.02.23
2023.02.21 일일 회고  (0) 2023.02.22
2023.02.20 일일 회고  (0) 2023.02.21

원시값 포장과 VO의 차이

사다리 미션을 진행하면서 모든 원시 값과 문자열을 포장한다 라는 요구사항을 받게 되었다.

원시 값을 포장하면서 "아 값을 객체로 포장하니까 값 객체 -> VO(Value Object)구나!" 라고 단순하게 생각했었다.

미션을 진행하면서 위치에 대한 값을 포장하여 vo 패키지에 넣었다. 위치는 변하는 값이다.
이때, 언뜻 보았던 VO는 불변성을 보장한다 라는 말이 생각났다. 그렇다면 원시값 포장과 VO는 다른 개념일까? 라는 생각을 하게 되었다.


먼저, VO와 원시값 포장의 정의에 대해 알아보고 어떠한 차이가 있는지 생각해 보자.

VO(Value Object)

값을 나타내는 객체로, 해당 객체의 상태를 변경하지 않는 불변 객체이며, 다음의 특징을 갖는다.

  1. equals & hash code 메서드를 재정의해야 한다.
    • 동등성을 보장해야 하기 때문에 equals & hash code 메서드를 재정의해야 한다.
  2. 수정자(setter)가 없는 불변 객체여야 한다.
    • 객체를 생성한 후 상태의 변경이 없다.

원시값 포장(Primitive value wrapping)

원시 타입(primitive type)의 데이터를 객체 형태로 감싸는 것을 말한다.

자바에서는 int, double과 같은 원시 타입을 객체로 감싸는 Wrapper 클래스를 제공한다. 이렇게 원시 타입을 객체로 감싸면, 해당 객체를 통해 데이터를 처리하는 로직과 데이터 자체를 나타내는 역할을 분리할 수 있다.

즉, VO는 값을 표현하기 위한 개념이고, 원시값 포장은 책임을 분리하기 위한 개념이다.

두 개념을 혼동하지 말자

디미터의 법칙

디미터의 법칙은 다른 객체가 어떠한 자료를 갖고 있는지 속사정을 몰라야 한다는 것을 의미하며, 이러한 이유로 Don’t Talk to Strangers(낯선 이에게 말하지 마라) 또는 Principle of least knowledge(최소 지식 원칙) 으로도 알려져 있다.

또는 직관적으로 이해하기 위해 여러 개의 .(도트)을 사용하지 말라는 법칙으로도 많이 알려져 있으며, 디미터의 법칙을 준수함으로써 캡슐화를 높여 객체의 자율성과 응집도를 높일 수 있다.

다음은 Player는 Name을 가지고 Name은 String을 래핑한 클래스다.

Public class Player {
    private final Name name;

    public Name getName() { return name; }
}

public class Name {
    private final String value;

    public String getValue() { return value; }
}

플레이어의 이름이 "maco"인지 비교하는 상황이라고 해보자.
이름을 꺼내서 사용하기 위해서는 player.getName().getValue().equals("maco"); 와 같이 사용해야 할 것이다.
player의 Name의 value를 꺼내와서 "maco"와 비교하라 라는 메시지로 볼 수 있는데, Player안의 Name이 있고 안에 value가 있다는 것을 알아야 한다.

이러한 경우에 객체 간의 결합도가 높아서 다른 객체에 대한 지식이 많아야 사용할 수 있다.
따라서 다음과 같이 메시지를 보내도록 수정함으로써 해결할 수 있다.

Public class Player {
    private final Name name;

    public boolean equalsName("String s") {
        s.equals(name.getName());
    }
}

player.equalsName("maco") 으로 내부적으로 Name이 어떻게 되어있는지 알 필요 없이 사용할 수 있게 되어 객체 간의 결합이 느슨해진 것을 볼 수 있다.

'회고 > 우아한테크코스' 카테고리의 다른 글

2023.02.27 일일 회고  (0) 2023.02.28
2023.02.23 일일 회고  (1) 2023.02.24
2023.02.21 일일 회고  (0) 2023.02.22
2023.02.20 일일 회고  (0) 2023.02.21
2023.02.17 일일 회고  (0) 2023.02.18

오전

Checked Exception(Compile Time Exception)

컴파일 시점에 Exception을 catch하는지 확인한다.
컴파일 시점에 Exception에 대한 처리(try/catch)를 하지 않을 경우 컴파일 에러가 발생한다.
Exception이 발생하는 메소드에서 throws 예약어를 활용해 Exception을 호출 메소드에 전달해야 한다.

Unchecked Exception(Runtime Tiem Exception)

컴파일 시점에 Exception을 catch하는지 확인하지 않는다.
컴파일 시점에 Exception이 발생할 것인지의 여부를 판단할 수 없다.
Exception이 발생하는 메소드에서 throws 예약어를 활용해 Exception을 처리할 필요가 없다. (처리해도 상관x)

Checked Exception과 Unchecked Exception 선택 방법

  • 호출하는 메소드가 Exception을 활용해 무엇인가 의미 있는 작업을 할 수 있을 때
  • 만약 호출하는 메소드가 Exception을 catch해 예외 상황을 해결하거나 문제를 해결할 수 없을 때
  • 명확하지 않을 때

오후

ArrayList & LinkedList구현

ArrayList와 LinkedList를 구현하기 위해 java.util.ArrayList를 분석하면서 재미있는 사실을 알아냈다.
다음은 ArrayList에서 배열의 크기를 늘려야 할 때 새로운 배열의 크기를 결정하는 메서드이다.

    private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity <= 0) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return minCapacity;
        }
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
    }

int newCapacity = oldCapacity + (oldCapacity >> 1); 부분을 보면 새로운 크기는 이전 배열의 크기 + (이전 배열의 크기 >> 1)로 설정한다.
즉 1.5배 크기의 배열을 생성한다는 것이다.
이전에는 2배라고 알고 있었는데 역시 코드를 직접 확인하는것이 제일 확실하다는 것을 느꼈다.

'회고 > 우아한테크코스' 카테고리의 다른 글

2023.02.23 일일 회고  (1) 2023.02.24
2023.02.22 일일 회고  (0) 2023.02.23
2023.02.20 일일 회고  (0) 2023.02.21
2023.02.17 일일 회고  (0) 2023.02.18
2023.02.16 일일 회고  (0) 2023.02.17

다른 크루들과 사다리 생성 미션에 관한 이야기를 하던 중 정적 팩터리 메서드 기법 에 대해 듣게 되었다.
정적 팩토리 메서드란 객체 생성의 역할을 하는 클래스 메서드이다.
이에 대해 자세히 알아보기 위해 이펙티브 자바를 읽었고, 읽다 보니 아이템1~5까지 읽게 되었다.

1. 생성자 대신 정적팩토리 메서드를고려하라

장점

  • 이름을 가질 수 있다.
  • 호출때마다 인스턴스를 새로 생성하지 않아도 된다.
  • 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다.
  • 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.
  • 정적 팩토리 메서드를 작성하는 시점에는 반환활 객체의 클래스가 존재하지 않아도 된다.

2. 생성자에 매개변수가 많다면 빌더를 고려하라.

점층적 생성자 패턴은 필요 없는 매개변수도 설정해주어야 한다.
복잡하고 읽기 어렵다
매개변수가 많아지면 힘들다

이럴 때 자바빈즈 패턴을 사용할 수 있다.
자바빈즈 패턴은 매개변수가 없는 생성자로 객체를 만든 후, setter 메서드를 호출해 원하는 매개변수 값을 설정하는 방식이다.
자바빈즈 패턴은 다음과 같은 단점이 존재한다.

  • 객체 하나를 만들려면 메서드를 여러 개 호출해야 한다.
  • 객체가 완성되기 전까지는 일관성이 무너진 상태에 놓이게 된다.
    → 클래스를 불변으로 만들 수 없으며 스레드 안전성을 얻으려면 추가 작업(freeze 등)을 해줘야 한다.

빌더 패턴과 자바 빈즈 패턴의 가장 큰 차이점은 앞에서도 말한, 불변성에 있다.
자바 빈즈 패턴은 객체를 생성한 '후', 값을 setter 메서드를 통해 넣는다. 그렇기에 객체 사용 도중 실수로, 혹은 악의적인 목적으로 setter 메서드를 통해 유효하지 않은 값이나 null값, 혹은 정확하지 않은 값이 들어갈 수 있다.
반면, 빌더 패턴은 객체 생성 '전', 값을 setter 메서드를 통해 넣는다. 그리고 다 넣었다면 마지막에 build 메서드를 호출하여 객체를 생성한다.
그렇기 때문에 객체 사용 중에 값이 변경될 우려가 없으며, 불변성과 안정성이 올라간다.
당연하지만, 빌더 패턴 사용 시에는 public setter 메서드를 선언해서는 안된다.

빌더패턴
builder를 이용해 필수 매개변수로 객체를 생성하고, 일종의 setter를 사용하여 선택 매개변수를 초기화한 뒤 build() 메서드를 호출하여 완전한 객체를 생성하는 패턴
생성자나 정적 팩터리가 처리해야 할 매개변수가 많다면 빌더 패턴을 선택하는 게 좋다.

3. private 생성자나 열거 타입으로 싱글턴임을 보증하라

public enum Elvis {
    INSTANCE; 

    public String getName() {
        return "Elvis";
    }

    public void leaveTheBuilding() { ... }
}
String name = Elvis.INSTANCE.getName();

Elvis 타입의 인스턴스는 INSTANCE 하나뿐. 더 이상 만들 수 없다.
복잡한 직렬화 상황이나 리플렉션 공격에서도 제2의 인스턴스가 생기는 일을 완벽히 막아준다.
단, 만들려는 싱글턴이 Enum 이외의 다른 상위 클래스를 상속해야 한다면 이 방법은 사용할 수 없다.

4. 인스턴스화를 막으려거든 private 생성자를 사용하라

추상 클래스로 만드는 것으로는 인스턴스화를 막을 수 없다.
하위 클래스를 만들어 인스턴스화하면 그만이다.
추상 클래스를 본 사용자도 상속해서 쓰라는 뜻으로 오해할 수 있다.

private 생성자를 추가하면 클래스의 인스턴스화를 막을 수 있다.

  1. 명시적 생성자가 private
    → 클래스 바깥에서 접근할 수 없다.
    Assertion Error를 던져 클래스 안에서 실수로라도 생성자를 호출하지 않도록 해준다.
    이 코드는 어떤 환경에서도 클래스가 인스턴스화되는 것을 막아준다.
  2. 상속을 불가능하게 하는 효과가 있다.
    private으로 생성자를 선언했으니 하위 클래스가 상위 클래스의 생성자에 접근할 길이 막혀버린다.

5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라

의존 객체 주입은 유연성과 테스트 용이성을 높여준다.

'회고 > 우아한테크코스' 카테고리의 다른 글

2023.02.22 일일 회고  (0) 2023.02.23
2023.02.21 일일 회고  (0) 2023.02.22
2023.02.17 일일 회고  (0) 2023.02.18
2023.02.16 일일 회고  (0) 2023.02.17
[레벨 1] 자동차 경주 미션 회고  (0) 2023.02.16

오전

좋은 코드에 대한 강의를 들었다.
아직은 감이 잘 오지 않아 키워드만 정리해 놓고 관련 책인 "좋은 코드, 나쁜 코드", "클린 코드"를 읽어 보며 다시 생각해보자.

가독성 높은 코드

  • 코드를 통해 의도를 전달하라
  • 일관된 코드 스타일을 가져가라
  • 하나의 역할만을 담당하라
  • 매개변수를 명확하게 하라
  • API를 적절하게 사용하라

예측 가능한 코드

  • 의미있는 값을 반환하라
  • 부수효과를 제거하라
  • 중요한 입력에 대해 무시하지 마라
  • 열것값을 암묵적으로 처리하지 마라
  • 열거형 사용 방식을 명확하게 하라

실수를 방지하는 코드

  • 원시값을 포장하라
  • 변경 가능성을 최소화하라

오후

좋은 회고

좋은 회고에 대한 강의를 들었다. 강의 내용 중 포비가 했던 말이 기억에 남는다.

좋은 피드백을 받고 싶다면 좋은 피드백을 먼저 하세요.

내가 받고 싶은 좋은 피드백이란 무엇일까 생각을 해보았다. 좋은 점과 함께 내가 개선했으면 하는 점을 이유와 함께 알려주는 것 이다. 상대의 단점을 이야기하는 것은 정말이지 쉽지 않다. 하지만 나에게 좋은 말만 해봐야 기분만 좋고 도움이 별로 안되듯이 다른 사람들에게도 아쉬웠던 점이 있으면 꼭 이야기 해주자. 우테코에서 페어프로그래밍을 많이 하면서 연습을 하면 나아지지 않을까?

'회고 > 우아한테크코스' 카테고리의 다른 글

2023.02.21 일일 회고  (0) 2023.02.22
2023.02.20 일일 회고  (0) 2023.02.21
2023.02.16 일일 회고  (0) 2023.02.17
[레벨 1] 자동차 경주 미션 회고  (0) 2023.02.16
2023.2.15 일일 회고  (0) 2023.02.15

+ Recent posts