Dion Quiz

  1. 접근 제한자를 언제 써야할까요? 왜 상태를 나타내는 변수는 private을 사용할까요? 이를 통해서 얻는 이점은 무엇일까요?

접근 제한자(한정자)는 캡슐화와 정보은닉을 구현할 수 있도록 해줍니다. 즉, 클래스 외부(사용자)에서는 내부 접근을 허용하지 않기 위해서입니다.

상태를 나타내는 변수는 정상적이지 않은 값으로 변경되면 안 되므로, 외부에서 함부로 접근하지 못하도록 private으로 접근을 제한합니다. 대신에 Getter를 통해서 값을 얻도록 읽기 권한만 허용할 수도 있고, Setter에서 값을 검증하여 사용자의 입력을 안정적으로 얻어낼 수 있습니다.

외부 사용자는 해당 클래스에 정의된 내부 메소드를 통해서 접근하므로 데이터 보호가 가능합니다. (정보 은닉) 또한, 객체의 수정사항이 필요할 때 기능별로 모듈처럼 수정하면 외부에서는 별도의 코드 수정 없이 사용 가능합니다. (유지 보수의 용이) 마지막으로, 내부 동작의 이해가 필요 없이 해당 메소드를 호출하면 된다는 점에서 사용자의 편의성도 제공해줄 수 있습니다.

  1. static은 언제 써야할까요? static을 어떻게하면 적게쓰면서 코딩을 할 수 있을까요? 왜 static을 많이쓰지 말라고 할까요?

static 키워드는 static 변수나 static 메소드를 선언할 때 사용합니다. 객체마다 가지고 있어야 할 데이터라면 인스턴스 필드로 선언하면 되지만, 객체마다 가지고 있을 필요성이 없는 공용적인 데이터라면 static 필드로 선언하는 것이 좋습니다.

메소드의 경우에는 인스턴스 필드를 이용해서 실행해야 한다면 인스턴스 메소드로 선언하면 되지만, static 변수를 사용한다면 반드시 static으로 선언해야 합니다. 이는 메모리에 로드되는 시점을 따져보면 당연합니다.

반드시 모든 인스턴스에서 공유해야 하고, 메모리상에 단 한 번만 로드되어야 하는 경우가 아니라면, 다른 방식으로 선언하는 것이 바람직합니다. 그렇게 하기 위해서는 클래스의 메소드를 통해서, 메소드의 매개변수를 통해서 데이터를 주고받을 수 있도록, 클래스와 메소드를 잘 설계해야 합니다.

static 키워드가 붙으면 메모리의 스태틱 영역에 저장되어 Garbage Collector의 관리 영역(Heap 영역)을 벗어납니다. 따라서 프로그램의 종료 시까지 메모리에 할당된 채로 유지되므로, 너무 자주 사용하게 되면 메모리의 낭비와 시스템 성능에 안 좋은 영향을 줄 수 있습니다.

  1. FileWriter와 BufferedWriter의 차이는 무엇이며, 두 클래스 중 무엇을 File IO에 사용하였나요? 그 이유는 무엇인가요?

FileWriter와 BufferedWriter는 성능의 차이가 있습니다. 이 성능의 차이는 버퍼의 크기 차이에서 발생합니다. 즉, FileWriter가 사용하는 버퍼의 기본 크기는 작은데, 이에 비해 BufferedWriter가 사용하는 버퍼의 크기는 충분히 큽니다. 통상적으로 100KB 이상의 데이터를 써야 하는 경우에는 BufferedWriter를 사용하는 것이 더 효율적입니다. (실제로, BuffereWriter는 문자 8,192개를 저장할 수 있는 버퍼의 크기를 가집니다.)

BufferedWriter bw = new BufferedWriter(new FileWriter("./data.txt", false));

따라서, 이처럼 FileWriter로 File객체를 받아오고, BufferedWriter로 File IO를 수행하였습니다.

https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/io/FileWriter.html

https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/io/BufferedWriter.html

https://www.programiz.com/java-programming/filewriter

https://www.programiz.com/java-programming/bufferedwriter

https://bcho.tistory.com/288

https://stackoverflow.com/questions/12350248/java-difference-between-filewriter-and-bufferedwriter

try-catch-resource

https://vmpo.tistory.com/entry/java-try-catch-with-resources-%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC

https://mangkyu.tistory.com/47

https://velog.io/@gillog/Java-%EC%A0%91%EA%B7%BC-%EC%A0%9C%ED%95%9C%EC%9E%90

https://gyrfalcon.tistory.com/entry/JAVA-%EC%A0%91%EA%B7%BC-%EC%A0%9C%ED%95%9C%EC%9E%90

1.

‘외부 사용자'는 어떤 블로그를 참고하다가 인용하게 된 표현인데, 저는 import 해서 사용하는 개발자들을 포함하는 말로 사용한 것인데, 혼동이 있었다면 그냥 ‘클래스 외부'라고만 표현하는 게 낫겠네요!

상태 값을 갖는 객체에 직접적인 접근을 하여 그 상태 값에 대해 로직을 수행하는 경우가 있는데, 그러한 경우 private로 선언했다고 하더라도 무분별한 getter의 사용은 옳지 않습니다. 좀 더 객체 지향적으로 생각해보면 그냥 상태 값을 가져오는(getter를 통해서) 것이 아니라, 상태 값을 ‘어떻게' 가져올지에 대한 메소드를 정의해놓고 그것을 통해서만 접근하는 것이 더욱 안전하고, 다른 객체와 메시지를 주고받을 때 객체 지향적인 프로그래밍이 가능해집니다.

https://woowacourse.github.io/javable/2020-04-28/ask-instead-of-getter

2.

static final은 수학에서의 파이와 같은 불변의 값, 상수(constant)를 저장할 때 사용합니다. static의 대표적인 특징은 프로그램상에서 단 하나만 할당된다는 것인데, final은 한 번 초깃값이 저장되면 변경할 수 없습니다. 따라서 한 번 저장되어 바뀔 필요가 없는 final 상수는 static으로 하나만 존재하는 것이 자연스러운 것입니다. 그렇기 때문에 static final은 자바에서 상수를 선언할 때 일반적인 표현입니다.

static 메소드는 인스턴스 없이 호출이 가능하며 (인스턴스에서 호출이 불가능), 유틸리티 함수를 만드는 데 유용합니다. 유틸리티 함수란 Math.max(), Math.min()처럼 여러 가지 계산과 처리를 대신에 해주는 라이브러리 함수들을 의미합니다. 그리고 내부 로직이 크게 변경될 일이 없는 메소드를 의미합니다. 사용이 적절한 구체적인 사례는 아래와 같습니다.

  • 이처럼 일반적으로 인스턴스가 생성되기도 이전에 호출되어야 하거나 다른 인스턴스의 생성에 의존하지 않는 경우
  • 인스턴스 변수를 사용하지 않는 경우
  • 모든 인스턴스 메소드 간에 쉽게 공유될 수 있을법한 코드인 경우
  • 내부 로직이 크게 변경될 일이 없거나, 오버라이딩 되지 않는 경우

https://stackoverflow.com/questions/2671496/when-to-use-static-methods

https://www.tutorialspoint.com/When-to-use-static-methods-in-Java

https://mygumi.tistory.com/253

p.s. Math.max() 같은 유틸리티 함수를 내가 작성할 일이 아예 없을 거라고 단정한 것 같은데, 실제로 개발하다 보면 사용하는 경우가 생기는가 보군요?

static 메소드는 사용하기에 편리하고 보기에 깔끔한 코드를 작성하는 데에 유리하지만, 객체 지향적이지 않다고 말하는 사람들이 많습니다. 가장 큰 문제는 상태를 가지고 있지 않고 단순히 메소드(로직 포함)만 가지고 있는 구조입니다. 따라서 이를 많이 사용하게 된다면 절차 지향적인 프로그래밍을 하는 것이 됩니다.

객체지향의 패러다임에서는, 객체를 설계하고 인스턴스 화 해서, 그것들로 하여금 원하는 시기와 방식으로 데이터를 관리할 수 있어야 합니다. 그러므로 static 메소드를 호출하는 대신 하고자 하는 동작을 미리 정의해놓고 노출한 객체를 만들어놓는 것이 바람직합니다. 유틸리티 함수를 사용하지 않는 것은 매우 힘든 일이지만, 사용하지 않도록 객체 지향적으로 변경하는 방법에 관해서 아래에 예시가 나와 있습니다.

https://www.yegor256.com/2014/05/05/oop-alternative-to-utility-classes.html

3.

현재는 텍스트 몇 줄이라서 1KB도 안 되는 용량입니다. 지금은 불필요한 오버헤드가 발생할 수 있겠네요! 그래서 지금은 그냥 학습하는 용도로만 사용해보도록 하겠습니다. 추후에 스케일이 커진다면 성능의 차이가 뚜렷해질 것 같습니다. 아래는 성능 비교를 직접 테스트해 본 블로그입니다. 저도 조만간 한번 직접 테스트해 보겠습니다! 사소한 성능 차이가 스케일에 따라 어마어마하게 커질 것으로 예상이 되네요.

https://bcho.tistory.com/288

FileWriter에서 append값을 false로 한 이유는 저 부분이 modify나 delete 메소드에서의 코드 일부를 발췌한 것인데, 파일 입출력으로 수정사항을 다시 통째로 덮어쓰는 방법을 사용했기 때문입니다. DB가 없을 때 어떻게 파일 내용 중 일부를 수정하고 삭제할 수 있을 것인가? 파일을 읽으면서 포인터값을 조작할 수 있는지에 대한 학습이 좀 더 필요할 것 같습니다. 그리고 호눅스가 추천해준 .csv나 .json 포맷으로 입출력한다면 그러한 부분이 더 수월할지도 공부해 보아야겠습니다.


와우, 정말 상세한 답변 감사합니다! 이번 퀴즈도 역시나 정말로 많은 도움이 되었어요!!

용어의 사용은 혼동을 주지 않으려면 정말 중요한 부분인 것 같아요!

private을 사용하는 이유는 기본적인 부분에 대한 설명을 제가 빼먹었네요. ‘주체적이다'라는 표현은 좋은 말이네요!

static과 final에 대해서는 충분히 이해했습니다. 감사합니다! 다만 스프링에서 좀 더 체감할 기회가 있어야 하겠네요.

‘절차 지향적'이라는 말은 저도 모르게 잘못 썼네요.. 용어 사용에 주의해야 겠습니다.

flag의 불필요한 명시가 오히려 가독성을 떨어뜨릴 수 있겠군요!

유틸리티 클래스도 좋은 방법이네요. 감사합니다! 정말 고마워요. @Dion