티스토리 뷰

우아한테크코스를 진행하며 접하게된 Java 개발관련 피드백과 팁들을 모아보자.

프로그래밍에 당연히 정답은 없다.
하지만 상황마다의 최선은 있으니까 이에 관한 내용들을 숙지하자.

머리속에 무의식적으로 있기도, 잊혀지기도 하므로
리스팅하고 가끔 한번씩 읽어보며 상기하자.

개발초반 연습을 위해 단정지어진or극단적인 몇가지 항목들도 포함되어 있다.
가장 자주 듣는 말이지만, 당연히 이 모든것들이 정답은 아니다


개발 피드백

■ 기능 목록을 유지한다.
기본적으로 기능목록 단위별로 commit한다.
계속해서 업데이트하는 살아있는 문서를 만들자.
구현하다보면 설계와 목록이 계속해서 바뀐다. 처음부터 완벽히 작성하려는 부담을 갖지말자
따라서 클래스와 메소드 구성까지 상세히 작성하지 않는다. 역시 자주 바뀌기 때문.
기능목록엔 예외상황도 포함, 역시 처음에 다 찾기 힘들기 때문에 구현하며 계속해서 추가

■ git commit 메시지를 신경써서 작업내용을 이해할수있도록 작성한다.

■ README.md를 통해 프로젝트 소개

■ 기능 목록과 별도로, 자주 쓰고 지우는 To do 리스트를 유지한다.

■ method와 class를 최대한 분리한다.

■ 함수가 한가지 일 잘하게 한다 !! ㄴ 각 method 10라인 & Indent 1 depth 제한해서 연습
ex: while문안에 if가 있으면 depth는 2다.

■ 반복과 중복은 악의 근원

■ 컨벤션을 지키자
Google Java Style Guide
자바 코딩 규칙(Java Code Conventions)

■ naming을 신경쓰자. 이름짓는데 시간을 투자하라. 이름으로 역할과 의도를 드러내고 축약하지 말자.
연속적인 숫자(a1, a2 ..)나 불용어(Info, Data, the, an ..) 등은 적절하지 못하다

■ 공백라인은 의도를 가지고 사용한다.
불필요한 공백라인을 만들지말고, 문맥이 달라지는 부분에 의도를 갖고 띄우자

■ 주석은 필요한 경우만
이름으로 의미가 드러난다면 굳이 주석을 달지않는다.
이름으로 가능한 의미를 드러내고 힘든경우 주석을 다는 연습

■ 문자열, 숫자 하드코딩 금지 (No magic number), 상수(static final)로 만들자

■ class내 순서는 상수/클래스변수 > 인스턴스변수 > 생성자 > public메소드 > private메소드 > @Override

■ 상태를 가지는 객체에서 데이터를 꺼내지말고(get), 메시지를 보내라
상태를 가지는 객체가 상태를 변경하는 것은, 자기자신에게 주도권이 있어야 한다.

private boolean isMaxPosition(Car car) {
  return car.getPosition() == maxDistance;
}

위와 같이 getter로 꺼내오기보다, 아래처럼 메시지를 보내 확인

car.isMaxPosition(maxDistance);

■ setter는 지양. 많은 경우 생성자로 대체가능. getter는 최대한 참다가 필요한 경우 만들어 쓴다.
set 메소드는 아무때나 호출될 수 있어 위험성이 존재하지만, 생성자는 맨처음에만 가능하다.
setter는 없이 코딩가능하다. getter는 최대한 자제하다 맨 뒷단의 DTO에서 view를 위해 꺼내쓰는것이 최선.

■ 한 클래스에 private 메소드가 많아지면, 새로운 클래스를 만들어 책임을 분담해야하는건 아닌지 고민한다

■ 필드(인스턴스변수)의 수를 최대한 줄이자
한 클래스당 최대 2개로 연습. 메소드로 대체가능하다

private List<Car> cars;
private List<String> winnerList;
private int maxDistance;

winnerList와 maxDistance는 cars만 있어도 아래와 같이 구해서 쓸수있다. (물론 그 사용빈도가 잦다면 변수로 만들어도 되고. 단 이때도 이대로 끝내지말고 , 이부분을 별도의 클래스로 분리할순없는지 고민해볼법)

private List<Car> cars;
private int getMaxDistance() { ... }
public List<String> getWinners() { ... }

■ Java API를 적극적으로 활용하자.
ex: String.join(), Collections.frequency() 등
for문대신 다음과같이 간단히 구현할수 있었다.

List<String> winners = Arrays.asList("pobi", "jason");
String result = String.join(",", winners);

■ 배열대신 Collection을 적극적으로 활용하자.
역시 이를 통해 다양한 API 사용가능

■ 일차적으로 Collection(List, Set, Map..)을 적극적으로 활용하고, 그래도 적절한 자료구조가 없다면 그땐 나만의 자료구조를 만들어서 사용하자. 즉, 클래스 추가하란 말

■ 핵심 로직 구현부와 View로직을 분리한다
비즈니스 로직과 UI 로직을 분리해, UI가 변경되더라도 핵심 비즈니스 로직을 재사용 가능하게 만든다.
단순히 클래스만 분리하는것이 아니라, 메소드 로직상에서도 도메인기능과 view기능을 분리한다.

MVC구조를 유지하면, 큰틀에서의 분리 가능

■ private, final을 향해 최대한 제한한다

■ 상수외의 인스턴스변수를 갖지않는 클래스라면 static으로 만들어도 괜찮다

■ 발생가능한 예외 케이스들을 적극적으로 고민해보자

■ 생성자에서의 유효성 체크

■ 원시타입과 문자열을 포장하라
객체로 포장하면, 돌아다니는 값들이 안전해진다.
ex. Money, LottoNumber class

■ else와 switch/case를 사용하지않는 연습을 해보자

■ 메소드의 인자수를 3개까지 제한해서 연습

■ 자료구조나 특정타입에 종속된 이름을 피한다.

String carNameList = scanner.nextLine();
String carNameString = scanner.nextLine(); 

비슷한 역할의 두 변수가 연달아 등장한다면?
애초에 해당 코드의 위치가 잘못되었거나, 별도 메소드로 분리해야했던것일수도. 다양하게 고민해볼것

■ class와 method를 분리하는 것을 꺼리지 말자
성능은 떨어지지만 아주 미미하다. (물론 대용량 환경에선 다르지만 그런것을 얘기하는것이 아니다)
HW보다 사람의 인건비가 중요해진 시대가 왔고, Clean Code를 향해서 ㄱㄱ

■ 자바의 제어자(Modifier) 순서

public protected private abstract default static final transient volatile synchronized native strictfp

■ 중복제거 : 상속(is-a 관계), 조합(has-a 관계)
중복을 제거하는 방법으로 상속과 조합이 있다.
상속은 알거고, 조합은 중복부를 별도 객체로 만들어 인스턴스로 갖도록 하는 것.
(현장에선 조합이 좀더 권유되긴한다고 한다.)

조합이 좀더 직관적이다

■ byPass 메소드를 꺼리지 말라
단순히 전달만 하는 byPass 메소드 생성을 꺼리지 말자.
객체1 -> 객체2 -> 객체3
(사다리 미션에서 Node.isConnected() ...? 메소드는 다시 Ladder로 물어봐서 전달만 하고 있다)

■ 상수와 exception은 따로 한곳에 모으기보다, 관련된 domain과 함께 두어 분산시키는 것이 더 낫다는 평이 다수인것 같다.

■ Exception

  1. Compile-time Exception
  2. Run-time Exception

(런타임 방식이 더 자주 쓰인다고 한다. 전자는 try-catch, throws 등을 강제해 논리적 오류를 더 철저하게 잡을 수 있을것 같았으나, 코드가 너무 복잡하고 더러워진다. 왠만하면 런타임방식을, 정말 중요해서 다른 api개발자에게 강제해야겠다 싶으면 컴파일타임 방식을 사용하자)
(가장 피해야할 것은, catch한 후 아무것도 안하는 것. 에러찾기 어려워진다)

■ 메소드의 인자가 많아지면 메소드를 분리하거나 객체로 묶을 수는 없을지 고민해봐야 한다.
https://www.matheus.ro/2018/01/29/clean-code-avoid-many-arguments-functions/

  1. Extract method technique : 별도 메소드로 추출한다.
  2. Parameter Object : 인자들을 묶어 별도 객체로 만든다.

■ 일급콜렉션은 필수 !!!

https://jojoldu.tistory.com/412

■ 유효성검사를 한쪽에 몰빵하지말고, 각 Layer에서 적절히 수행

■ 점진적인 리팩토링 - 과도기적 중간단계를 갖는다
메서드 인자를 추가하거나 타입이 변경될때, 또는 객체로 포장할때 등 기존코드에서 수정해야할때
이곳저곳을 정신없이 수정하다보면 빠뜨리기도 쉽고 중간과정에서 계속해서 컴파일 에러가 난다.
이때 원래 메서드는 그대로 두고 "메서드2"를 만들어 땅따먹기 하듯이 점차 수정해나가다, 다 고치면 메서드1을 find usage 체크후 최종적으로 메서드2를 rename로 고치는 방법을 사용한다.
훨씬 안정적이며, 이런 과도기적 중간단계는 반드시 필요한 과정이다.


개발 팁

■ 반복문 대신 재귀함수를 활용할수도있다

private long inputPrice() {
  String price = "";
  do {
    System.out.println("구입 금액을 입력해 주세요.");
    price = SCANNER.nextLine();
  } while (!Validator.isNaturalNumber(price));
  return Long.parseLong(price.trim());
}
private static long getPrice() {
  try {
    System.out.println("구입 금액을 입력해 주세요.");
    return Long.parseLong(SCANNER.nextLine());
  } catch (IllegalArgumentException e) {
    return getPrice();
  }
}

■ 기본생성자와 오버로딩한 또다른 (인자 받는)생성자가 존재한다면, 일반적으로 작은곳에서 큰곳을 호출한다.

■ assertEquals보다 assertThat을 사용해보자
assertThrow, isEqualTo, isTrue 등 다양하게 사용할것

■ 이름짓기 방식
단순히 Money.getMoney()보다 Money.get()과 같은 방식도 상황에 따라 ㄱㅊ
역시 객체의 용도가 제한적이라면 printLotto()를 toString()으로 overriding했던 느낌 기억
ㄴ 다른 피드백을 보니, 실제 객체의 중요정보를 표현한다면 toString()으로, 오로지 View를 위한 표현로직이라면 View에서 갖는게 맞는것 같다.

■ 문자열 비교시, 상수를 앞으로 변수를 뒤로 위치시켜 NPE를 방지한다 ( ex: "abc".equalsOf(name) )

■ List를 외부에 공개할땐, 변경하지 못하도록 unmodifiableList()로 리턴해도 괜찮다

■ enum 활용도 고려해보자

■ stream도 활용해보자.
단, 과도한 stream 사용에 대한 이슈가 있는것 같으니 주의하며

■ return this vs return new Car(this.name, this.position)의 차이
둘 중 무엇이 더 나은것이 아니다. 역시 상황에 따른 선택
전자는 인스턴스를 그대로 전달하기에 리소스 절약과 성능은 더 좋을 수 있다.
후자는 같은기능이지만 새로 생성해 전달하기에 성능은 떨어져도 버그확률이 적어짐. 다른곳에서 position이 변경될 가능성 등, 불변객체를 향해.

■ enum을 활용해 if문을 없앨 수 있다

Java) enum을 활용해 if문 없애기 (with Calculator 예제)

■ String의 경우, 하드코딩보단 StringUtils.isBlank(), isEmpty()를 활용해보라

Apache Library - StringUtils Class

■ 생성자대신 정적팩토리 메서드를 고려해보자
1주차 - 2장 객체 생성과 파괴 - 재성 - SLiPP 스터디 - SLiPP::위키

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함