DFS, BFS를 첫 입문할 때 하기 좋은 그래프 문제이다. 해당 문제는 DFS적으로 접근하면 Depth를 잘 활용할 수 있었는데, BFS를 활용해서 풀어볼 때 문제가 생겼었다. 직면했던 문제는 BFS에서는 Depth를 어떤 기준으로 체크할 수 있을까? 라는 고민을 많이 하게 한 문제였다.
내가 만질 때는 null을 Queue에 넣어서 구분자로 생각하고 null을 만날 때마다 count가 1씩 증가하도록 구현했었는데, 좀 더 직관적인 방법이 좋았을거라 생각했고, 스터디를 진행하는 다른 사람이 푼 것을 봤을 때 distance라는 배열을 만들어서 해당 값을 누적되게 구현했는데 더 좋은 방법 같았다.
스프링 공부를 하면서 final키워드를 만났을 때 평소와 다른 어색한 사용방법 때문에 의문점이 생겼다.
final인데 왜 주입이 되는거지?
@Controller
public class MemberController{
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService){
this.memberService = memberService;
}
}
final 선언 후 첫 값 초기화는 가능해서 그런건가? 라는 의문이 들었다.
이유는, 평소 final키워드 선언할 때는 항상 선언과 동시에 값을 초기화하기 때문이다.
이러한 부분이 모호하다 생각해서 final과 함께 서비스를 공부해보았다.
final
final 키워드는 변경이 더이상 필요없을 때 선언해줄 수 있다.
이번에 스프링부트를 사용하며 의문이 들었기에 위의 코드를 기준으로 스프링부트에서 생성자주입을 할때,
우리는 해당 객체의 메서드의 변경이 더이상 필요없다고 인지를 하고 있을 것이다.
때문에 Service객체에서 사용되는 모든 메서드들은 어디에서 사용이 되던 동일한 역할과 책임이 있다.
그러한 이유로 다른 컨트롤러든 서비스에서든 해당 Service에서 나올 기댓값은 동일하기에 클래스 추가생성을 통해 자원을 낭비할 필요가 없으니 final 선언을 하는 것이고, 이것이 싱글톤형태로 사용되고있는 이유다.
여러 클래스에서 해당 Service클래스를 여러번 선언하고 생성자주입을 한다면 싱글톤으로 돌아갈 수 없다고 생각될 수 있다.
하지만 이 부분은 스프링 빈의 특성을 통해서 해결이 가능하다 스프링 빈은 기본적으로 싱글톤 스코프로 관리되기 때문에 한번 해당 클래스가 스프링 빈으로 등록이 된다면 이후 동일한 인스턴스가 계속 동작한다
하나의 도메인에서 다른Repository를 불러오는 것이 응집도를 올린다고 생각했고, 또 Repository에 직접 접근하는 것은 막아야 한다고 생각했기에 xxxxRepository를 그대로 써오는 것에서 xxxxService로 변경해 사용하는 것으로 바꿔 생각했다.
이 과정에서 우리는 Service부분의 클래스 생성을 xxxxRepository → xxxxService로 변경, 생성자 주입을 시켰는데 문제가 발생했다..!
결론부터 말하자면 @RequiredArgsConstructor의 특성을 제대로 생각하지 못한 것이 문제였고 자세한 이유는 아래에서 다루겠다.
그래서
처음에 원인 파악을 할 때 해당 클래스들의 생성자 주입에서 final이 빠졌다고 인지를 아예 못 한 상황이였고, 때문에 한참을 뻘짓 한 것 같다.
그 때 당시에 코드변경 후 직접 실행해 확인하지 않고, 바로 테스트코드를 작성하고 있어서 해당 UserService 자체가 문제라고 인지하지 못했고,
테스트코드 작성도 익숙치 않은 상태라 테스트코드를 못짜서 그렇다고 판단을 했고, 테스트 환경을 좀 더 공부하는 뻘짓(?)을 했고, 해당 뻘짓을 할 때 디버그를 통해 알아낸 정보는 아래의 사진과 같았다.
Service에 있는 클래스가 모두 null로 되어있는 것… 이걸 발견하고도 한참 뻘짓하기도 하고 기초중의 기초적인 실수였지만....
그래도! 발견한 것에 의미를 두자! 라고 생각을 하기로 했다.
정리해서 말하자면 @RequiredArgsConstructor 어노테이션의 특성이 생성자 주입 시 final선언을 해줘야 자동으로 생성자 주입을 하고 스프링 빈을 생성하는데, final이 없어 생성자 주입을 하지 않았기 때문에 각 Service들이 null상태로 있었던 것이고, 이러한 문제가 발생했었던 것이다.
처음에는 소수판별이기 때문에 이전에 배워뒀던 에라토스테네스의 체를 사용하려고 했다. 하지만 이 문제는 메모리제한이 4MB에 N의 범위가 8 즉 최대 10,000,000 천만의 범위이기 때문에 자바의 데이터 최소단위 byte - boolean크기: 1byte로 지정되었을 때 에토체의 boolean배열만 어림잡아 100mb가 넘게된다 때문에 기각.
일반 소수판별 메서드를 생성해서 처리했다.
hint
DFS, 소수판별을 해야한다
Solution
최종적으로 가지치기 등등 다양하게 하니까 속도가 좀 빨라졌다. 1,3,5,7,9가 아닌 0~10까지 돌렸을 때는 속도가 좀 느렸었다.