본문 바로가기

TIL

[2022-1-8] 스프링 핵심 원리 - 기본편 : 스프링 컨테이너와 스프링 빈 (1/2) / 프로그래머스 연습문제 - 폰켓몬(Set, HashSet)

스프링 핵심 원리 - 기본편(스프링 컨테이너와 스프링 빈 1/2)

저번시간에 AppConfig에 @Configuration 과 @Bean 애너테이션을 추가해서 ApplicationContext를 통해 스프링 컨테이너를 생성했다. 오늘은 컨테이너가 생성되면 어떻게 Bean이 생성되고 조회할 수 있는지 공부했다.

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

이 코드를 통해 스프링 컨테이너를 생성한다. 스프링 컨테이너 안에는 Spring Bean 저장소가 있다. 컨테이너를 생성할 때 파라미터로 넘어온 AppConfig 클래스 통해 스프링 빈을 등록한다. 빈의 이름은 @Bean으로 등록된 메소드의 이름, 그 이름에 해당하는 객채는 메소드의 반환값에 의해 등록된다.

 

그 후 이렇게 등록된 Bean 객체들과 의존관계에 있는 객체들이 Bean에 등록되고 연결된다. 

 

이렇게 등록된 Bean들은 다양한 방식으로 잘 등록이 되었는지 조회할 수 있다.

 

이에 대해 몇가지 테스트를 진행했다.

  • 모든 빈을 출력하고, 내가 직접만든 애플리케이션 빈만 출력, 이름 또는 타입으로 조회하는 테스트
  • 이름이 존재하지 않을 경우, 같은 타입이 둘 이상 있을 경우 오류 테스트
  • 빈이름을 다르게 지정해서 둘이상의 같은 타입에 대응하는 테스트
  • 특정 타입만 모두 조회하는 테스트

이에 대한 코드는 아래와 같다.

https://github.com/unannn/spring-core-basics/tree/main/core/core/src/test/java/hello/core/beanfind

 

GitHub - unannn/spring-core-basics: 스프링-핵심-원리-기본편 실습 코드

스프링-핵심-원리-기본편 실습 코드. Contribute to unannn/spring-core-basics development by creating an account on GitHub.

github.com

 

프로그래머스 코딩테스트 연습문제 - 폰켓몬

오늘 푼 문제는 폰켓몬 이었다. 결국 요점은 중복이 있는 배열에서 중복없는 정수의 개수를 찾는 것이었다. 순서는 상관없고, 중복없는 원소의 개수가 필요하므로 자바의 Set을 사용해 쉽게 풀 수 있었다. 풀이 코드는 아래와 같다.

import java.util.Set;
import java.util.HashSet;

class Solution {
    public int solution(int[] nums) {
        Set<Integer> bucket = new HashSet<>();
        for(int num : nums){
            bucket.add(num);
        }

        int bucketSize = bucket.size();
        int maximum = nums.length / 2;
        return bucketSize < maximum ? bucketSize : maximum;
    }
}

처음으로 Set<> 을 사용해봤다. 구현 객체는 HashSet을 사용했다. HashSet, TreeSet, LinkedHashSet등의 구현 클래스가 있지만 일단 HashSet의 성능이 가장 좋다고 해서 사용했고, 왜 성능이 좋은지에 대해 한번 알아보고자 한다.

 

일단 HashSet 클래스의 생성자를 살펴봤다.

HashSet 클래스 생성자

HashSet은 HashMap의 인스턴스를 멤버변수에 저장하고 사용한다. 아직 HashMap에서 깊게 공부해보진 않았지만 일단키(key)와 값(value)를 갖는 구조라는 것은 알고 있었다. 하지만 Set에는 이러한 개념이 존재하지 않았다. 어떻게 이 부분을 처리했는지는 클래스를 살펴보면서 알 수 있었다. 아래 이미지는 HashSet 클래스의 PRESENT 멤버 상수이다.

HashSet 클래스의 PRESENT 멤버 상수

이미지에서 볼 수 있듯이 Object 객체를 static final 멤버로 갖고 있다. 주석에서 설명하는 것 처럼 이 상수는 더미 값으로 HashMap의 value 값으로 사용된다. 아래 이미지를 보면 어떻게 사용되는지 알 수 있다.

HashSet 클래스의 add() 메소드

HashMap에 구현된 put() 메소드는 데이터 저장에 성공하면 null을 반환하고, 실패하면 이전 value값,  즉 HashSet에서 PRESENT로 정의된 Object 객체를 반환한다. 이와같은 방식으로 HashSet은 HashMap의 key값만을 통해 데이터를 저장한다.

 

HashMap의 구현에 대해서는 다음에 문제풀면서 HashMap을 직접적으로 사용해보게 됐을 때 해보려고 한다. 아래는 HashMap이 어떻게 동작하는지 코드를 분석해놓은 글이다. 간단하게 정리하면 HashMap은 각 노드들을 노드 배열에 저장하고 해싱을 통해 탐색, 삽입 하기 때문에 O(1) 의 시간복잡도를 가질 수 있다는 것이다.

https://sabarada.tistory.com/57

 

[자료구조] 코드로 알아보는 java의 Hashmap

HashMap이란 HashMap은 Key, Value를 저장하는 Map의 구현체 중 하나입니다. 자료구조에 Key를 넣으면 Value를 반환하도록 합니다. 그리고 HashMap은 Key를 Hashing을 하여 저장하여 빠르게 처리 그리하여 HashMa..

sabarada.tistory.com

 

결론 및 느낀점

스프링

점점 스프링의 처음부터 수준을 올려가는 것이 재밌었다. 하지만 그만큼 스프링 내부적으로 어떻게 코드가 되어있어서 이런 방식으로 작동하는지 모르는 것에 대해서 조금 답답함이 느껴지는 것 같다. 자바 공부를 조금 더 하고나서, 스프링프레임워크의 구조에 대해서도 한번 직접 코드를 보면서 공부해보고 싶다.

프로그래머스

HashSet 을 처음 사용해봤고 사용하니까 문제가 너무 쉽게 풀려서 좋았다. 또 내부에서 HashMap을 사용하여 구현되어 있고, value를 Object 객체를 static final 로 선언하여 모든 value 들을 갖게 해놓고 key 값만 사용하는 점도 재밌었다. 그런데 HashMap의 구조가 생각보다 많이 복잡해서 깊게 분석해보지는 못했던거같다. 다음에 정말 HashMap을 사용하는 문제를 만나면 그날은 다른 공부 다 접어놓고 한번 깊게 파봐야겠다.