전체 코드 중 주요 기능으로 잡은 에어비엔비 Room CRUD 부분을 정리해보려 한다.

+ 추가기능 ( 페이징 처리, 검색어 입력, S3, 게시글 좋아요, 비회원처리 ) 

 

Dto, Entity 등은 제외하고 Controlle / Repository / Service 부분만 정리


Room Controller

1. 숙소등록 부분

  • 등록 부분이기 때문에 @PostMapping 어노테이션 사용
  • RoomService 의존성 주입
  • Json형식과 Form Data 형식 같이 받아오기
    • (Value="/이름", consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE}})
    • @RuquestPart로 Json 형식인 RoomRequestDto와 MultipartFile 형태인 이미지를 받아온다.
    • (value = "data"),(value = "file")을 통해 Json, FormData 각각 key값을 명시해 준다.
  • @AuthenticationPrincipal 스프링시큐리티를 이용해 인증된 유저정보를 가져온다.

 

2. 숙소 정보 조회 부분

  • 조회 부분이기 때문에 @GetMapping 어노테이션 사용
  • Pageable을 이용하여 페이징처리 기능을 구현
  • 비회원인 경우 토큰값이 필요 없기 때문에 @AuthenticationPrincipal 유저정보를 매개 값에서 빼준 형식으로 작성
  • 키워드 조회의 경우 쿼리문에 사용할 String keword값이 추가된다.

 

3. 숙소정보 수정 / 삭제 부분 

  • @PathVariable로 게시글 고유 id 값을 조회하여 해당 게시글을 불러온다
  • 작성과 마찬가지로 @RequestPart 어노테이션을 사용하여 Json 형식과 FormData 형식을 불러온다.
  • @CrossOrigin은 CORS오류를 해결하는 방법 중 하나인데, 사실 이번프로젝트는 config 파일에서 CORS에러처리를 해주었기 때문에 필요는 없지만 참고용으로 달아보았다.
  • 삭제 부분은 Request가 따로 필요없기 때문에 유저정보와 해당 글을 불러 올 @Pathvariable만 필요하다.

 

4. 숙소 좋아요 취소기능 부분

 

Repository

1. RoomRepository

  • @Repository 어노테이션으로 빈에 등록해주기.
  • JpaRepository<>를 상속받고 있다.
  • 윗 쿼리문은 전체 조회시 사용 된다.
  • 아래 쿼리문은 검색어 입력후 조회시 사용 된다.

 

2. RoomLikeRepository

  • 가장 윗 쿼리문은 해당 게시글 좋아요 추가 기능에 사용된다.
  •  아래 쿼리문은 해당 게시글 좋아요 취소 기능에 사용된다.
  • 가장 아래 쿼리문은 게시글 삭제 부분에서 좋아요도 삭제해주기 위해 사용된다.

 

3. ImageFileRepository

  • 위의 쿼리문은 게시물을 수정하고, 게시글을 삭제하 때  삭제할 이미지 파일을 조회하면서 하나씩 DB에서 불러오는 데 사용된다.
  • 아래 쿼리문은 위의 쿼리문으로 불러운 이미지를 삭제할 때 사용된다.

 

Service

1. 의존성 주입

 

2. 숙소 정보 작성 (Create)

  • 먼저 Room테이블에 작성자로부터 받은 requestDto와 유저 정보를 저장
  • 이미지 파일이 있다면, S3Service부분에 만들어준 upload메소드를 이용하여 S3와 이미지 테이블에 입력받은 데이터 저장
  • return은 프론트엔드와 메시지 그리고 Status Code로 맞췄기 때문에 미리 만들어준 StatusMsgCode enum을 이용하여 반환

 

 3. 숙소 정보 전체 조회 (로그인 했을 경우 - 토큰유효)

  • Spring Data JPA 에서 제공하는 Pageable 인터페이스를 이용해 페이지처리를 해준다.
  • Room테이블에 저장되어 있는 게시글들을 찾아서 불러오고
  • RoomResponseDto로 되어있는 빈 배열을 하나 만들어준다.
  • 테이블에서 불러온 Room테이블을 바깥 for문에서 돌려주면서 엔티티를 디티오 타입으로 변환시켜 준다.
  • 그리고 이중 for문을 사용하여 각 게시글에 달려있는 이미지파일들을 String 타입으로 변화시켜 path 라고 지정한 url 값만 새로 만들어준 imageFileList배열에 넣어준다.
  • 최종적으로 roomResponseDto에 담겨진 room 정보와, 그에 딸린 좋아요 여부/ 좋아요 수, 그리고 이미지 파일까지 같이 return해준다.

 

4. 비회원일 경우 전체 조회

  • 로그인 후 전체조회와 구조는 같지만, 매개값에 user 정보만 빠져있는 형태이다.
  • 참고로 이전에는 이미지 파일의 path(url의미)를 객체타입으로 줬는데, 프론트엔드에서 String 타입이 더 가공하기 쉽다고 하여 아랫 부분에 String 타입으로 다시 배열을 만들어 주었다.

 

5. 키워드 검색

  • 전체 조회 부분에 keyword 매개 값이 추가된 형태이다.
  • Containing 쿼리문을 이용하여 roomrepository에서 관련된 키워드만 꺼내 올 수 있다.

 

6. 숙소 정보글 수정 

  • @Transactional 어노테이션을 까먹지 말자!
  • 가장 먼저 해당하는 게시글이 있는지 여부를 파악하고 없다면 만들어둔 CustomException을 던져준다.
  • 이어서 해당 게시글의 작성자와 수정하려는 자가 일치하는지 파악하고 다르다면 또한 예외처리를 던져주었다.
  • 이어서 받아온 Json타입의 request들을 먼저 update메소드를 통해 업데이트 해준다.
  • 다음, 해달 게시글에 이미지파일이 있다면(if 문), 해당 글에 달려있던 이미지 파일을 조회해서 먼저 S3에서 삭제를 해준다.
  • 이어서 DB에 저장되어있는 파일도 삭제해준다.
  • 그리고 새로 받은 이미지 파일을 upload메소드를 통해 S3와 DB에 업로드 한다.
  • try- catch 문을 사용하여 과정에 문제가 있다면 예외처리를 해주고, 없다면 return 값으로 StatusMsgCode를 반환한다.

 

7. 숙소 글 삭제

  • @Transactional 어노테이션을 까먹지 말자!
  • 게시글과 작성자 정보를 조회하는 것까지 수정 부분과 동일하다.
  • 가장 먼저 게시글에 달려있는 좋아요를 삭제한다.
  • 다음으로 이미지 파일을 삭제한다. 순서는 해당 글의 이미지 파일을 DB에서 조회하고, S3에서 먼저 삭제해 준 다음 DB에서도 삭제한다.
  • 마지막으로 Room테이블에서 해당 글을 삭제해 준다.
  • 수정글과 마찬가지로 try-catch문을 사용하여 예외처리를 해주고 return값을 주었다.

 

8. 좋아요 추가 / 삭제

  • @Transactional 어노테이션을 까먹지 말자!
  • roomLikeRepository에서 user정보와 게시글 정보를 조회하여 해당 like를 조회한다.
  • boolean 타입으로 해당 글의 좋아요 여부를 확인한다. 좋아요가 추가된 상태인 경우 true를 반환 한다.

 

  • 먼저 해당 게시글이 존재하는지 체크하고 없을 경우 예외를 날려준다.
  • 위에서 만들어둔 checkLike 메소드를 이용해, 만약 값이 true라면 (즉, 이미 좋아요를 한 상태라면) 만들어둔 StatusMsgCode 를 이용해 상태코드와 함께 "이미 좋아요를 추가했습니다." 라는 예외를 날려 준다.
  • 예외처리가 되지 않은 경우 saveAndFlush 메소드를 통해 좋아요 추가 정보를 저장해준다.

 

  • 먼저 해당 게시글이 존재하는지 체크하고 없을 경우 예외를 날려준다.
  • 위에서 만들어둔 checkLike 메소드를 이용해, 만약 값이 false라면 (즉, 이미 좋아요를 취소한 상태라면) 만들어둔 StatusMsgCode 를 이용해 상태코드와 함께 "이미 좋아요를 삭제했습니다." 라는 예외를 날려 준다.
  • 예외처리가 되지 않은 경우 deleteByRoomIdAndUserId 쿼리를 통해 좋아요를 DB에서 삭제해 준다.

※ Config 파일 : 스프링 시큐리티 인증/ 인가를 다루는 파일

 

import 부분

 

@EnableGlobalMethodSecurity

  • Use EnableMethodSecurity instead. Enables Spring Security global method security similar to the <global-method-security> xml support. More advanced configurations may wish to extend GlobalMethodSecurityConfiguration and override the protected methods to provide custom implementations.

 

@EnableWebSecurity

  • is used for spring security java configuration. Add this annotation with @configuration on top of your security java class that extends WebSecurityConfigurerAdapter. Override the configure(WebSecurity web) & configure(HttpSecurity http) . WebSecurityConfigurerAdapter를 상속받은 config 클래스에 @EnableWebSecurity 어노테이션을 달면SpringSecurityFilterChain이 자동으로 포함됩니다.

 

passwordEncoder / securityFilterChain

 

  • passwordEncoder : 비밀번호 암호화
  • antMatchers() 로 지정할 수 있는 항목
    • hasRole() or hasAnyRole() : 특정 권한을 가지는 사용자만 접근할 수 있습니다.
    • hasAuthority() or hasAnyAuthority() : 특정 권한을 가지는 사용자만 접근할 수 있습니다.
    • hasIpAddress() : 특정 아이피 주소를 가지는 사용자만 접근할 수 있습니다.
    • permitAll() or denyAll() : 접근을 전부 허용하거나 제한합니다.
    • rememberMe() : 리멤버 기능을 통해 로그인한 사용자만 접근할 수 있습니다.
    • anonymous() : 인증되지 않은 사용자가 접근할 수 있습니다.
    • authenticated() : 인증된 사용자만 접근할 수 있습니다.

 

corsConfigurationSource

  • corsConfigurationSource : CORS 에러 해결을 위한 부분

 

CORS란?

- HTTP 요청은 기본적으로 Cross-Site HTTP Requests가 가능하다. Simple 하게 다른 도메인의 Resource를 사용하는것을 의미한다. 하지만 Cross-Site HTTP Requests는 Same Origin Policy를 적용 받기 때문에 요청이 불가하다. 즉 프로토콜, 호스트명, 포트가 같아야만 요청이 가능하다.

 

 

SPA(Single Page Application) 개발이 보편적으로 이루어 지고있어서 Front , Back사이에 도메인이 달라지는 경우가 많다 이경우에는 CORS 허용 정책이 필요하다.

 

서비스 배포 후 최종 버전

@Configuration
@RequiredArgsConstructor
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig {
    private final JwtUtil jwtUtil;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // CSRF 설정
        http.csrf().disable();

        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.httpBasic().disable()
                .authorizeRequests()
                .antMatchers("/api/users/**").permitAll()
                .antMatchers(HttpMethod.GET, "/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilterBefore(new JwtAuthFilter(jwtUtil),UsernamePasswordAuthenticationFilter.class);
        http.cors();

        return http.build();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource(){

        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOrigin("http://localhost:3000"); // 프론트엔드 로컬서버
        config.addAllowedOrigin("http://the-greatest-minkyu.s3-website.ap-northeast-2.amazonaws.com/"); // 프론트엔드 S3서버
        config.addExposedHeader(JwtUtil.AUTHORIZATION_HEADER);
        config.addAllowedMethod("*");
        config.addAllowedHeader("*");
        config.setAllowCredentials(true);
        config.validateAllowCredentials();
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);

        return source;
    }

}

[ 참고자료 ]

 

 

 

1. 어려웠던 부분 : 코드는 어느정도 구현이 된 상태라 Refactoring 작업을 시작하려했다. Api Rseponse를 사용하여 반환타입을 맞추고 싶었는데, 생각보다 손봐야 할 부분이 많기도 하고, 꼭 지금 코드에 그리고 서비스에 필요한 부분은 아닌 것 같아 구현을 취소하게 되었다. 구현은 어렵지 않아보였는데, 시간상 비효율적인 것 같아 사용을 포기했다. 다음 프로젝트땐 사용해봐야 겠다.

 

2. 느낀 점 : 백엔드 인원이 많아 프론트엔드보다 상대적으로 시간이 많다보니 새로운 기능을 많이 시도해 볼 수 있었다. 오늘은 내가 맡은 추가기능인 검색어 기능을 구현했는데, 직접 구현에 성공해서 뿌듯했다 : ) 

 

3. 새로 알게 된 내용 :  Cotaining()을 사용해 키워드 검색 기능을 구현할 수 있다. 자세한 내용은 제일 아랫부분 링크 참조 ! 프로젝트 처음 스코프는 무조건 회원가입을 한 생태에서 진행되는 스코프였는데, 시간이 남아서 비회원일 경우를 생각해 코드를 Refactoring 하는 작업을 했다. 비회원과 회원전용 API를 구현하는 방법은 몇 가지가 있는데 다음과 같다.

  1. API 분리
  2. 토큰 유무정보를 확인한 후 API 접근
  3. 유저정보를 프론트에서 -1 이나 0으로 받아서 비회원임을 판가름하는 방법

 

4. 셀프칭찬 (오늘 잘한 일) : 내가 맡은 부분을 완료하고, 추가로 다른 분이 담당하신 코드를 뜯어보는 시간을 가졌다. 팀으로 프로젝트를 진행하다보니, 내가 맡지 않은 부부은 놓치고 가기 쉬운데 코드 리뷰를 통해 다양한 기능을 공부하는 나를 칭찬한다.

 

5. 내일 할 일 : 프로젝트 자잘한 오류 수정, 프론트엔드와 합쳐보기


[오늘 공부한 부분] 

 

[04] OAuth 2.0 카카오 로그인 구현

 

[04] OAuth 2.0 카카오 로그인 구현

User Entity 일반 로그인 회원가입 엔티티에 kakaoId 추가 그리고 아래에 카카오로그인과 업데이트에 사용할 생성자도 추가해 준다. UserController kakaoService 의존성 주입 kakao login Controller 부분 UserReposito

leejincha.tistory.com

[05] Spring 페이징처리 + 키워드 검색기능 구현

 

[05] Spring 페이징처리 + 키워드 검색기능 구현

Controller //숙소 전체 조회 @GetMapping("/rooms") // size '/api/rooms?page=0&size=3' public ResponseEntity getRooms(@AuthenticationPrincipal UserDetailsImpl userDetails, @PageableDefault(sort = "createdAt", direction = Sort.Direction.DESC) Pageable p

leejincha.tistory.com

 

  • 후입선출(LIFO : Last In First Out) : 나중에 넣은 객체가 먼저 빠져나가는 자료구조
  • 선입선출(FIFO : First In First Out) : 먼저 넣은 객체가 먼저 빠져나가는 자료구조
  • 컬렉션 프레임워크에는 LIFO(리포) 자료구조를 제공하는 Stack 클래스와 FIFO(피포) 자료구조를 제공하는 Queue 인터페이스를 제공한다.

https://carminati.altervista.org/PROJECTS/C++/FIFO%20and%20LIFO/FIFOandLIFO.html

 

Stack

① Stack 클래스는 LIFO 자료구조를 구현한 클래스. 다음은 주요 메소드 이다.

리턴타입 메소드 설명
E push(E item)  주어진 객체를 스택에 넣습니다.
E peek()  스택의 맨 위 객체를 가져옵니다. 객체를 스택에서 제거하지 않습니다.
E pop()  스택의 맨 위 객체를 가져옵니다. 객체를 스택에서 제거합니다.

 

 

② Stack 객체를 생성하려면 저장할 객체 타입을 E타입 파라미터 자리에 표기하고 기본 생서자를 호출하면 된다.

예를 들어 String을 저장하는 Stack은 다음과 같이 생성할 수 있다.

 

③ 사용 예는 다음과 같다.

public class Coin {
    private int value;

    public Coin(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}
public class StackExample {
    public static void main(String[] args) {
        Stack<Coin> coinBox = new Stack<>();

        coinBox.push(new Coin(100));
        coinBox.push(new Coin(50));
        coinBox.push(new Coin(500));
        coinBox.push(new Coin(10));

        while (!coinBox.isEmpty()) {
            Coin coin = coinBox.pop();
            System.out.println("꺼내온 동전 = " + coin.getValue());
        }
    }
}

<실행결과>
꺼내온 동전 = 10
꺼내온 동전 = 500
꺼내온 동전 = 50
꺼내온 동전 = 100

 

Queue

① Queue 인터페이스는 FIFO 자료구조에서 사용되는 메소드를 정의한다. 다음은 Queue 인터페이스에 정의되어 있는 메소드이다.

리턴 타입  메소드  설명 
boolean  offer(E e)  주어진 객체를 넣는다. 
peek()  객체 하나를 가져온다. 객체를 큐에서 제거하지 않는다. 
pool()  개체 하나를 가져온다. 객체를 큐에서 제거한다. 

 

② Queue 인터페이스를 구현한 대표적인 클래스는 LinkedList 이다. LinkedList는 List 인터페이스를 구현했기 때문에 List 컬렉션이기도 하다. 아래는 LinkedList 객체를 Queue 인터페이스로 변환한 것

 

③ 사용 예 - 먼저 넣은 메시지가 반대쪽으로 먼저 나오기 때문에 넣은 순서대로 메시지가 처리된다.

public class Message {
    public String command;
    public String to;

    public Message(String command, String to) {
        this.command = command;
        this.to = to;
    }
}
import java.util.LinkedList;
import java.util.Queue;

public class QueueExample {
    public static void main(String[] args) {

        Queue<Message> messageQueue = new LinkedList<>();

        messageQueue.offer(new Message("sendMail","홍길동"));
        messageQueue.offer(new Message("sendSMS","신용권"));
        messageQueue.offer(new Message("sendKakaotalk","홍두께"));

        while (!messageQueue.isEmpty()) {
            Message message = messageQueue.poll();
            switch (message.command) {
                case "sendMail" :
                    System.out.println(message.to + "님에게 메일을 보냅니다.");
                    break;
                case "sendSMS" :
                    System.out.println(message.to + "님에게 SMS을 보냅니다.");
                    break;
                case "sendKakaotalk" :
                    System.out.println(message.to + "님에게 카카오톡을 보냅니다.");
                    break;
            }
        }
    }
}

<실행결과>
홍길동님에게 메일을 보냅니다.
신용권에게 SMS을 보냅니다.
홍두께님에게 카카오톡을 보냅니다.

 


참고자료 : <혼자 공부하는 자바> - 신용권

'Coding > Java' 카테고리의 다른 글

[33] JAVA 보조 스트림  (2) 2023.01.24
[32] JAVA 입출력 스트림  (0) 2022.12.28
[30] JAVA 컬렉션 프레임워크  (0) 2022.12.25
[29] JAVA 스레드 제어  (0) 2022.12.21
[28] JAVA 멀티스레드  (0) 2022.12.21
  • 컬렉션 프레임워크(Collection Framework) :자료구조를 사용해서 객체들을 효율적으로 추가, 삭제, 검색할 수 있도록 인터페이스와 구현 클래스를 java.util 패키지에서 제공하는 것
  • 컬렉션 : 객체의 저장을 의미
  • 프레임워크 : 사용 방법을 정해놓은 라이브러리를 의미
  • 컬렉션 프레임워크의 주요 인터페이스로는 List, Set, Map이 있다. 

https://www.toolsqa.com/java/data-structure/

 

List 컬렉션

  • List 컬렉션은 배열과 비슷하게 객체를 인덱스로 관리한다.
  • 배열과의 차이점은 저장 용량(capacity)이 자동으로 증가하며, 객체를 저장할 때 자동 인덱스가 부여된다는 점이다.
  • 객체 자체를 저장하는 것이 아니라 객체의 번지를 참조한다는 특징이 있다.
  • 그렇기 때문에 동일한 객체를 중복 저장할 수 있는데, 이 경우 동일한 번지가 참조된다.

 

< List 컬력션에서 공통으로 사용 가능한 List 인터페이스의 메소드 >

기능 메소드 설명
객체 추가 boolean add(E e)  주어진 객체를 맨 끝에 추가합니다.
void add(int index, E element)  주어진 인덱스에 객체를 추가합니다.
E set(int index, E element)  주어진 인덱스에 저장된 객체를 주어진 객체로 바꿉니다.
객체 검색 boolean contains(Object o)  주어진 객체가 저장되어 있는지 조사합니다.
E get(int index)  주어진 인덱스에 저장된 객체를 리턴합니다.
boolean isEmpty()  걸렉션이 비어 있는지 조사합니다.
int size()  저장되어 있는 전체 객체 수를 리턴합니다.
객체 삭제 void clear()  저장된 모든 객체를 삭제합니다.
E remove(int index)  주어진 인덱스에 저장된 객체를 삭제합니다.
boolean remove(Object o)  주어진 객체를 삭제합니다.

※ 위의 표에서 E라는 타입 파라미터는 저장되는 개체의 타입을 List 컬렉션을 생성할 때 결정하라는 뜻

 

ArrayList 

  • List<String> list = new ArrauList< >( );
  • 기본 생성자로 ArrayList 객체를 생성하면 내부에 10개의 객체를 저장할 수 있는 초기 용량을 가지게 된다.
  • ArrayList에서 특정 인덱스의 객체를 제거하면 바로 뒤 인덱스부터 마지막 인덱스까지 모두 앞으로 1씩 당겨진다. 마찬가지로 특정 인덱스에 객체를 삽입하면 해당 인덱스부터 마지막 인덱스까지 모두 1씩 밀려난다.
  • 따라서 빈번한 객체 삭제와 삽입이 일어나는 곳에서는 ArrayList를 사용하는 것 보다 LinkedList를 사용하는 것이 좋다.
  • 반면, 인덱스를 이용해서 객체를 찾거나 맨 마지막에 객체를 추가하는 경우는 ArrayList가 더 좋은 성능을 발휘한다.

Vector

  • List<E> list = new Vector<E>( );
  • ArrayList와 다른 점은 Vector는 동기화된 메소드로 구성되어 있기 때문에 멀티 스레드가 동시에 Vector의 메소드를 실행할 수 없고, 하나의 스레드가 메소드 실행을 완료해야만 다른 스레드가 메소드를 실행할 수 있다는 점이다.
  • 멀티 스레드 환경에서 안전하게 객체를 추가, 삭제할 수 있다. = 스레드 안전(thread safe)

LinkedList

  • ArrayList는 내부 배열에 객체를 저장해서 관리하지만, LinkedList는 인접 참조를 링크해서 체인처럼 관리한다.
  • List<E> list = new LinkedList<>( );
  • 특정 인덱스의 객체를 제거하면 앞뒤 링크만 변경되고 나머지 링크는 변경되지 않는다.
  • 끝에서부터(순차적으로) 추가 또는 삭제하는 경우는 ArrayList가 빠르지만, 중간에서 추가나 삭제하는 경우는 앞뒤 링크 정보만 변경되는 LinkedList가 더 빠르다.

 

Set 컬렉션

  • List 컬렉션은 객체의 저장 순서를 유지하지만, Set 컬렉션은 저장 순서가 유지되지 않는다.
  • 객체를 중복해서 저장할 수 없고, 하나의 null만 저장할 수 있다.
  • Set 컬렉션에는 HashSet, LinkedHashSet, TreeSet 등이 있다.
  • 인덱스로 관리하지 않기 때문에 인덱스를 매개값으로 갖는 메소드가 없다.

< Set 인터페이스의 메소드 >

기능 메소드 설명
객체 추가 boolean add(E e)  주어진 객체를 저장합니다. 객체가 성공적으로 저장되면 true를 리턴하고 중복 객체면 false를 리턴합니다.
객체 검색 boolean contains(Object o)  주어진 객체가 저장되어 있는지 조사합니다.
boolean isEmpty()  컬렉션이 비어있는지 조사합니다.
lterator<E> iterator()  저장된 객체를 한 번씩 가져오는 반복자를 리턴합니다.
int size()  저장되어 있는 전체 객체 수를 리턴합니다.
객체 삭제 void clear()  저장된 모든 객체를 삭제합니다.
boolean remove(Object o)  주어진 객체를 삭제합니다.

 

  • Set<String> set = ...;
  • Set 컬렉션은 인덱스로 객체를 검색해서 가져오는 메소드가 없기 때문에, 대신 전체 객체를 대상으로 한 번씩 반복해서 가져오는 반복자 Iterator를 제공한다.
  • Iterator<String> iterator = set.iterator( );

< Iterator 인터페이스에 선언된 메소드 >

리턴 타입 메소드 설명
boolean hasNext()  가져올 객체가 있으면 true를 리턴하고 없으면 false를 리턴한다.
E next()  컬렉션에서 하나의 객체를 가져온다.
void remove()  Set 컬렉션에서 객체를 제거한다.
while(iterator.hasNext()){ //저장된 객체수 만큼 루핑
	String str = iterator.next(); //String 객체 하나를 가져옴
    if(str.equals("지니")){ 
    	iterator.remove(); //객체를 제거
    }
}

 

HashSet

  • Set<E> set = new HashSet<E>();
  • 객체들을 순서 없이 저장하고 동일한 객체는 중복 저장하지 않음
  • 객체를 저장하기 전에 먼저 객체의 hashCode()메소드를 호출해서 해쉬코드를 얻어내고, 이미 저장되어 있는 객체들의 해시코드와 비교한다. 만약 동일한 해시코드가 있다면 다시 equals() 메소드로 두 객체를 비교해서 true가 나오면 동일한 객체로 판단하고 중복 저장을 하지 않는다.
  • 문자열을 저장할 경우, 같은 문자열을 갖는 String 객체는 동등한 객체로 간주된다.

 

Map 컬렉션

  • key(키) 와 value(값)로 구성된 Map.Entry 객체를 저장하는 구조로 Entry는 Map 인터페이스 내부에 선언된 중첩 인터페이스이다.
  • 키와 값은 모두 객체이다.
  • 키는 중복 저장될 수 있지만, 값은 중복 저장될 수 없다.
  • 만약 기존에 저자된 키와 동일한 키로 값을 저장하면 기존의 값은 없어지고 새로운 값으로 대체된다.
  • HashMap, Hashtable, LinkedHashMap, Properties, TreeMap 등이 있다.

 

< Map 컬렉션의 메소드 >

기능 메소드 설명
객체 추가 V put(K ket, V value)  주어진 키로 값을 저장합니다. 새로운 키일 경우 null을 리턴하고 동일한 키가 있을 경우 값을 대체하고 이전 값을 리턴합니다.
객체 검색 boolean containsKey(Object key)  주어진 키가 있는지 여부를 확인합니다.
boolean containsValue(Object value)  주어진 값이 있는지 여부를 확인합니다.
Set<Map.Entry<K,V>> entrySet()  키와 값이 쌍으로 구성된 모든 Map.Entry 객체를 Set에 담아서 리턴합니다. 
V get(Object key)  주어진 키가 있는 값을 리턴합니다.
booleab isEmpty()  컬렉션이 비어있는지 여부를 확인합니다.
Set<K> keySet()  모든 키를 Set객체에 담아서 리턴합니다.
int size()  저장된 키의 총 수를 리턴합니다.
Collection<V> value()  저장된 모든 값을 Collection에 담아서 리턴합니다.
객체 삭제 void clear()  모든 Map.Entry(키와 값)를 삭제합니다.
V remove(Object key)  주어진 키와 일치하는 Map.Entry를 삭제하고 값을 리턴합니다.

 

HashMap

  • HashMap의 키로 사용할 객체는 hashCode()와 equals()메소드를 재정의해서 동등 객체가 될 조건을 정해야 한다.
  • 객체가 달라도 동등 객체라면 같은 키로 간주하고 중복 저장되지 않도록 하기 위함이다.
  • 키와 값의 타입은 기본타입을 사용할 수 없고 클래스 및 인터페이스 타입만 사용 가능하다. 
  • 키로 String 타입을 사용하고 값으로 Integer타입을 사용하는 HashMap은 다음과 같이 생성할 수 있다.
  • Map<String, Integer> map = new HashMap<>();

Hashtable

  • HashMap과의 차이점은 Hashtable은 동기화된 메소드로 구성되어 있기 때문에 멀티 스레드가 동시에 Hashtable의 메소드를 실행할 수 없고, 하나의 스레드가 실행을 완료해야만 다른 스레드를 실행할 수 있다는 것
  • 멀티스레드 환경에서 안전하게 객체를 추가, 삭제할 수 있기 때문에 Hashtale은 스레드에 안전하다.
  • Map<String, Integer> map = new Hashtable<>();

'Coding > Java' 카테고리의 다른 글

[32] JAVA 입출력 스트림  (0) 2022.12.28
[31] JAVA LIFO와 FIFO 컬렉션  (0) 2022.12.25
[29] JAVA 스레드 제어  (0) 2022.12.21
[28] JAVA 멀티스레드  (0) 2022.12.21
[27] JAVA 기초 복습  (0) 2022.12.14

+ Recent posts