✅ 문제 


 

  • 제한 사항 

 

  • 입출력 예시 

 

문제가 이해하기 어렵지는 않은데 주의할건 배열을 자를때 query의 원소값은 인덱스 값이라는 것이다. 

 

짝수 인덱스에서는 query[i] 의 값의 다음 인덱스 부터 없애야하고, 홀수 인덱스에서는 query[i] 값 이전을 없애야한다. 

 

 

✅ 풀이 


 

import java.util.*;
class Solution {
    public int[] solution(int[] arr, int[] query) {
        for(int i =0; i<query.length; i++) {
            if(i % 2 == 0) {
                arr = Arrays.copyOfRange(arr, 0, query[i]+1);
            }else {
                arr = Arrays.copyOfRange(arr, query[i], arr.length);
            }
        }
        return arr;
    }
}

 

 

처음 생각했던 방법은 query를 순회하면서 인덱스를 짝수와 홀수로 구분한다. 

인덱스가 짝수인 경우에는 query[i] 값+1 이후의 값을 버리면 되기 때문에  copyOfRange 메서드를 사용하여 (처음~ query[i] +1) 범위까지 잘라서 배열에 담는다. 

똑같은 방식으로 인덱스가 홀수인 경우에는 query[i]의 값 이전의 값을 버리기 위해 시작 지점은 query[i]로 설정한다. 

 

 

사실 문제가 어렵지 않았기 때문에 풀이과정을 떠올리는것은 쉬웠다. 그런데 이 문제는 자세히 보면 배열의 값들을 굳이 삭제할 필요가 없다, 

 


 

😲 이 문제는 사실 투 포인터 

 

배열을 자르는 방식을 생각해보면 짝수인덱스 일때는 해당 인덱스의 원소값부터 뒷쪽을 자르고, 홀수 인덱스 일때는 해당 인덱스의 원소값 이전값들을 자른다. 

그렇다면 원본 배열에서 어떤 인덱스 부터 ~ 어떤 인덱스 까지 출력할지 인덱스값을 정해줄 수도 있다. 

즉, 계속해서 배열을 삭제를 할 필요 없이 시작과 끝 값만 알면 된다. 

 

 

그렇다면 출력을 할 시작 인덱스와, 끝 인덱스를 설정하여 해당 문제를 풀어보자. --->  투 포인터

import java.util.*;

class Solution {
    public int[] solution(int[] arr, int[] query) {
        
        int start = 0;
        int end = arr.length - 1;  

        for (int i = 0; i < query.length; i++) {
            if (i % 2 == 0) {
                end = start + query[i];  
            } else {
                start += query[i];  
            }
        }
        return Arrays.copyOfRange(arr, start, end + 1);
    }
}

 

 

 

 

 

start 인덱스는 배열의 첫번째 원소부터, end 인덱스는 배열의 마지막 인덱스 부터 시작한다.

 

  • query 인덱스가 짝수인 경우, end 인덱스를 조정
  • query 인덱스가 홀수인 경우, start 인덱스를 조정

 

여기서 생각해볼 것은 start 인덱스의 위치가 배열의 시작 위치가 된다는 점이다. 

 

 

이 예시를 가지고 생각해봤을때,  start 인덱스의 처음 시작값은 0이다. 

따라서 query[1] = 4가 가리키는 값은 arr[4] =4 이다. 

그런데 query[1]=1 로 인해  start =1 이 된다. 이제 배열의 시작점은 바로 원본 배열의 arr[1] 에서 시작해야한다.

 

만약 시작 위치를 조정해주지 않는다면 arr[2]= 2 의 값이 되므로 [1,2,3,4] 에서 [1,2] 라는 값이 나오게 된다. 

시작 위치를 arr[1] 의 위치로 바꾸면,  2번 인덱스가 가리키는 값은 3이 되므로 [1,2,3] 으로 올바른 값이 나올 수 있게 된다. 

 

 start 인덱스의 범위가 달라질때 마다 해당 지점이 새로운 배열의 시작점이 되어야하기 때문에 query[i]의 값을 누적해서 더해야한다.

end 인덱스의 값은 결국 달라진 시작점에서 부터 세어줘야하기 때문에 end = start+query[i] 로 설정해준다. 

 

 

✅ 정리 


 

문제의 조건만 보고 단순하게  기준을 세워서 배열을 잘라내야겠다라고 풀이 과정을 생각했었는데, 투 포인터를 사용했다면 굳이 배열을 자를 필요가 없었다. 

 

겉으로 봤을때는 투포인터를 생각하지 못했지만 이렇게도 투포인터가 쓰인다는것을 알게되었다

문제 

 

 

풀이 

class Solution {
    public int[] solution(int[] arr) {
        int[] answer = {};
        int start_index=-1;
        int end_index=-1;
        
        // start_index값 찾기
        for(int i=0;i<arr.length;i++){
            if(arr[i]==2) {
                start_index=i;
                end_index=start_index; // 2가 배열에 하나인 경우를 고려
                break;
            }
        }
        
        // 빈 배열인지 확인
        if(start_index==-1) return new int[] {-1};
        
        
        // end_index값 찾기
        for(int i=start_index+1;i<arr.length;i++){
            if(arr[i]==2){
                end_index=i;
            }
        }
        
        // answer 배열 생성
        int answer_length = end_index - start_index + 1;
        answer = new int[answer_length];
        
        for(int i=start_index;i<=end_index;i++){
            int begin=i-start_index;
            answer[begin]=arr[i]; 
        }
        
        return answer;
    }
}

 

처음 이 문제를 봤을때 생각했던건 처음 2가 나온 인덱스를 체크하고, 마지막으로 2가  나오는 인덱스를 체크하여 두 인덱스 값에 해당하는 원소값들을 정답 배열에 넣으면 되겠다고 생각했다. 

그래서 처음 2가 나온 곳을 가리기는 포인터와 마지막으로 2가 나온 곳을 가리키는 포인터 두개가 있으면 문제가 쉽게 풀리겠다고 생각하여 투 포인터로 풀어야 겠다고 생각했다. 


참고 코드 

 

방법1 

import java.util.*;
class Solution {
    public int[] solution(int[] arr) {
        int[] answer = {};
        int start = -1;
        int end = -1;

        for(int i=0;i<arr.length;i++){
            if(arr[i]==2){
                if(start==-1){
                    start = i;
                }
                end = i;
            }
        }

        if(start==-1){
            answer = new int[]{-1};
        }else{

            answer = Arrays.copyOfRange(arr,start,end+1);
        }



        return answer;
    }
}

 

 

방법2

import java.util.*;
class Solution {
    public ArrayList<Integer> solution(int[] arr) {
        ArrayList<Integer> answer = new ArrayList<Integer>();
        int start = -1;
        int end = -1;
        for(int i = 0; i<arr.length; i++){
            if(arr[i]==2){
                start = i;
                break;
            }
        }
        for(int i = arr.length-1; i>=0; i--){
            if(arr[i]==2){
                end = i;
                break;
            }
        }
        if(start==-1 && end==-1){
            answer.add(-1);
        } else{
            for(int i = start; i<=end; i++){
                answer.add(arr[i]);
            }
        }

        return answer;
    }
}

 

 

처음에 나오는 2를 가리키는 인덱스를 어떻게 고정해야할지에 대한 고민이 있었는데 방법1의 코드를 보면 start==-1 라는 조건을 설정하여 처음 2가 나온 인덱스의 위치를 저장하고, 이후에 원소값이 2인 인덱스의 경우에는 start의 값이 -1 이 아니므로 end 값에 저장된다. 

계속해서 배열의 크기 만큼 반복문을 수행하면서 end의 값이 새롭게 갱신되어 반복문이 종료된 시점에 end에는 마지막에 나온 2의 인덱스 위치가 저장되게 된다. 

 

물론 방법2의 코드 처럼 start와 end의 값을 서로 다른 for문을 이용해서 각각 구해도 시간 복잡도는 동일하겠지만, 위의 조건식 하나로 하나의 for문에서 두 값을 결정할 수 있어서 해당 풀이가 좋다고 생각했다. 

 

또한 나의 경우 end 지점을 결정할때 (처음 2가 나온 인덱스 다음 부터 ~ 배열의 끝 까지) 라고 범위를 설정하고 end값을 구했었는데, end의 의미를 생각해보면  end 는 가장 마지막에 나오는 2의 인덱스 값을 저장하면 된다. 

그렇기 때문에 앞에서 부터 값을 찾는것이 아닌 배열의 끝 부터 시작하여 하나씩 값을 내려오는게 더 빨리 end값을 찾는 길이다. 

그래서 두번째 코드에서 이러한 부분을 담고 있어서 배울점이 있는 코드라 넣어봤다. 

 

컨트롤러는 클라이언트의 요청을 받아 필요한 자원을 응답해주는 API이다. 

 

우리가 컨트롤러 코드를 작성하고 나서 제대로 동작하는지 확인하기 위해서는 POSTMAN 과 같이 요청을 보내는 프로그램을 통해서 직접 요청을 보내고 , 응답이 잘 오는지 확인해봐야한다. 

그런데 요청을 받기 위해서는 서버가 동작중 이어야 하므로 매번 서버를  띄우고 직접 주소로 요청을 보내는 방식으로 테스트를 하려니 너무 번거로웠다. 

 

그래서 테스트 코드가 필요한것이다. 테스트 코드를 작성해두면 굳이 서버를 직접 띄우지 않고, 직접 요청을 하지 않더라도 내가 만든 API가 제대로 동작하는지 점검할 수 있다. 

 

먼저 컨트롤러 API가 제대로 동작하는지 확인 할 수 있는 테스트 코드 짜는 방법을 알아보자. 

 

그런데 서비스 클래스는 로직을 검사하는 방식으로 테스트 코드를 작성하면 되는데 컨트롤러의 API 경우 클라이언트의 요청을 제대로 전달 받는지 확인해야한다. 그렇다면 서버가 필수적이지 않을까? 어떻게 서버를 띄우지 않고도 테스트를 할 수 있다는 것일까? 

 

그 이유는 MockMvc 라는 클래스 덕분이다. 

MockMvc는 애플리케이션을 서버에 배포하지 않고도 테스트용 Mvc환경을 만들어 요청,전송,응답의 기능을 제공한다. 

 

MockMvc 클래스를 사용하기 위해 @AutoConfigureMockMvc 어노테이션을 이용하여 자동으로 MockMvc 객체를 생성하고 구성할 수 있도록 한다. 

 

@SpringBootTest // 테스트용 스프링 컨테이너 생성
@AutoConfigureMockMvc // MocMVC 생성 및 자동 구성
class BlogApiControllerTest {

    @Autowired
    protected MockMvc mockMvc;

    @Autowired
    protected ObjectMapper objectMapper;  // 직렬화,역직렬화를 위한 클래스

    @Autowired
    private WebApplicationContext context;

    @Autowired
    private BlogRepository blogRepository;

    @BeforeEach
    public void mockMvcSetup(){
        // mock 객체 설정
        this.mockMvc= MockMvcBuilders.webAppContextSetup(context)
                .build();
        // 초기화
        blogRepository.deleteAll();
    }


    @DisplayName("글 생성 api 테스트")
    @Test
    public void addArticle() throws Exception{

        //given - 요청 설정
        final String url = "/api/articles";
        final String title = "제목";
        final String content = "내용";
        final AddArticleRequest request = new AddArticleRequest(title, content);

        // 객체를 json으로 직렬화
        final String requestBody = objectMapper.writeValueAsString(request);


        // when 설정한 내용으로 요청 전송
        ResultActions result = mockMvc.perform(post(url)
                .contentType(MediaType.APPLICATION_JSON_VALUE)
                .content(requestBody));

        //then
        result.andExpect(status().isCreated());

        List<Article> articles = blogRepository.findAll();

        assertThat(articles.size()).isEqualTo(1);
        assertThat(articles.get(0).getTitle()).isEqualTo(title);
        assertThat(articles.get(0).getContent()).isEqualTo(content);
    }
    
}

문제 상황 

ec2 서버를 실행 시킨 후에  포스트맨을 이용하여 서버에 요청을 보내려고 하였다. 

하지만 포스트맨에서 send를 클릭하니 아래와 같은 화면이 나왔다. 

분명 서버도 실행되고 있고 서버 주소도 올바른데 왜 이런 오류가 날까... king 받쥬

 

 

 

해결 방법

https://jemmaa.tistory.com/9

 

POSTMAN error: connect ETIMEDOUT 해결

AWS EC2 IP를 이용해서 POSTMAN에 send를 했는데 error: connect ETIMEDOUT 에러가 떴다. 첫번째로 확인해야할 것은 AWS EC2에서 인바운드 규칙에서 해당 port를 열었는지 확인해야할 것이다. 나는 port를 5001을 썼

jemmaa.tistory.com

 

8080 포트를 열어놓지 않아서 요청이 가지 않았던것 이었다

 

 

햅삐 ~ 

 

 

디버깅

디버깅이란 버그를 잡아내는 과정을 의미한다

즉, 코드에 있는 문법상의 오류나 논리의 오류를 잡아내는 과정을 디버깅이라고 한다.

 

코드에서 발생하는 문법상의 오류는 컴파일러가 알아서 경고해주기 때문에 발견하기 쉽지만 우리가 짠 코드에서 발생하는 논리 오류는 컴파일러가 알려주지 않기 때문에 디버깅 과정을 통해 스스로 찾아내야 한다.

 

디버깅 하는 방법

오류가 발생했을 것이라 예상되는 지점에 중단점(break point)를 찍고 IDE의 디버깅 기능을 실행하면 된다.

디버깅을 하는 자세한 방법은 각자가 사용하는 컴파일러에 따라 조금씩 다를 수 있으므로 검색하여 찾아보면 된다

 

 

디버깅을 해야 하는 이유

  • 컴퓨터가 코드를 읽는 방법대로 생각할 수 있게 됨
  • 알고리즘의 동작 원리를 확실히 이해할 수 있음

 

디버깅 활용 사례 - 코딩 테스트에서 실수하기 쉬운 4가지 오류

 

변수의 초기화

특히 반복문에서 한번 반복을 하고 빠져나와서 초기화 해줘야 하는 변수를 초기화 해주지 않아서 발생하는 오류

 

 

인덱스 범위 실수나 비교 연산자 실수

반복문의 시작 값이나 끝 값의 범위를 잘못 지정했거나, 비교 연산자( < , <=) 오류

혹은 배열의 초기화 과정 중에서 범위를 잘못 지정해주는 경우 등이 있다.

 

 

잘못된 변수 사용

사용하고자 하는 변수를 착각해 다른 변수를 써주는 경우

 

 

자료형 범위 오류

음수가 나올 수 없는데 음수가 나오면 변수 범위 초과를 의심해보자

'알고리즘' 카테고리의 다른 글

시간 복잡도  (0) 2024.08.03

시간복잡도란 연산의 수행 횟수를 의미한다.

일반적으로 컴퓨터는 1초에 1억 번 정도의 연산을 수행한다.

 

시간 복잡도 표기법

  • 빅-오메가 표기법 : 최선일 때 연산 횟수
  • 빅-세타 표기법 : 평균일 때 연산 횟수
  • 빅-오 표기법 : 최악일 때 연산 횟수

코딩 테스트에서는 빅-오 표기법을 기준으로 시간 제한을 고려하는 것이 좋다. 가장 연산이 오래 걸리는 테스트 케이스를 기준으로 알고리즘을 작성한다면 나머지 테스트들은 당연히 통과 할 수 있기 때문이다.

 

표기 방법은 중괄호 안에 최악일 때의 연산 횟수를 적어주면 된다.
연산 횟수가 N번 이라면 O(N) , N*N 이라면 O(N*N)으로 작성한다.

 

 

시간 복잡도 계산법

 

시간 복잡도를 계산하는 방법은 가장 많이 중첩된 반복문의 횟수가 시간 복잡도의 기준이 된다.

또한 시간 복잡도 계산에서  상수는 무시한다. 

 

예를들어 데이터의 개수인  n = 1억 이라고 생각해보자.

  • O(n*n) = 1억 x 1억
  • O(n) = 1억

1억 이라는 상수는 그 자체로 유의미한 값이라고 생각 할 수 있지만 시간 복잡도가 O(n*n)인 1억 x 1억 의 값과 비교했을때 충분히 무시해도 될 만큼 작은 값이다.

 

그렇다면 여기서 1억에 100을 곱하면 어떨까?

  • O(n)
  • O(100*n)

위의 두 값은 유의미한 차이가 있을까?

물론 n 앞의 상수가 n의 값보다 커진다면 n*n 보다 커지므로 유의미한 값이 될 수 있다.

하지만 n =1억 인데 앞의 상수가 10 이라고 해서 두 시간 복잡도 사이에 큰 차이가 있지는 않을 것이다.

 

따라서 시간 복잡도를 계산할때는 가장 많이 중첩된 반복문 을 기준으로 생각하자.

 

 

 

 

그렇다면 우리는 왜 코딩 테스트를 볼 때 시간 복잡도를 고려해야할까?

 

 

 

알고리즘 선택의 기준

코딩 테스트의 경우에는 데이터 개수와 시간 제한이 주어지기 때문에 제한 시간 내에 연산을 할 수 있도록 시간 복잡도를 고려하여 알고리즘을 선택 하는 것이 중요하다.

 

예를들어 데이터의 개수가 100만이고 제한 시간이 1초라고 하자.

여기서  시간복잡도가 O(n*n)인 알고리즘을 사용한다면 문제를 통과 할 수 있을까? 출력 결과는 맞을 수 있어도 시간 초과로 인해 틀리게 될 것이다. 단순히 출력값이 일치 한다고 해서 통과 할 수 없다 

이런 경우에는 O(n*logn) 이하의 시간 복잡도의 알고리즘을 선택해서 코드를 작성해야 문제를 풀 수 있을 것이다. 

 

코딩 테스트 문제를 풀때는 데이터 개수와 시간 제한을 생각하여 어떠한 시간 복잡도를 가지는 알고리즘을 쓸 지 선택하는 것도 중요하다 

 

비효율적인 코드의 로직 수정

만약 작성한 코드가 시간 초과로 틀렸다면 시간 복잡도를 고려하여 어디에서 가장 연산이 많이 걸렸는지를 생각하고

더욱 코드를 효율적으로 작성 할 수 있을 것이다.

 

시간 초과로 문제를 틀렸다면 가장 연산이 많이 걸리는 부분의 코드를 수정해야지 괜히 출력을 더 빨리하는 방법을 쓰도록 수정 한다고 해서 테스트를 통과 할 수는 없을 것이다. 비효율적인 부분을 개선하는 것이 가장 효율적인 방법이다. 

'알고리즘' 카테고리의 다른 글

디버깅  (0) 2024.08.04

 

 메서드에서 무언가 검증하는 기능을 만들때 validate 를 쓸지 verify를 쓸지 헷갈렸다. 

사실 한국어로 생각했을때 두 의미는 비슷비슷 해보여서  그때 그때 맘에드는걸 골라서 썼었는데 확실히 정리하는게 좋을 것 같다. 

 

 

validation

사용자가 입력한 값이 맞는지 검증할때 사용한다. 

사용자가 서버로 값을 보낼때 맞춰야하는 조건들을 확인할때 사용 ( ex 이메일 서식, 6~10자리 이하의 수를 입력해주세요, 비밀번호에는 영소문자가 포함되어야합니다 이런거 체크할때) 

 

verification 

개발자가 만든 로직이 맞는지 검증할때 사용한다. 

ex) 아이디 중복 검증, 토큰 검증 등등 개발자가 사용자로 부터 넘어온 값을 확인해줘야하는 부분

 

 


 

 

GPT 형님 답변~ 틀릴 수도 있음~ 

 

Validate vs. Verify

  1. Validate: Validation is the process of checking if something meets a set of criteria or standards. It's about ensuring the data or system input conforms to the rules and requirements set before processing. Validation typically happens before any processing takes place.
    • Example: When a user enters their email address during registration, the system checks if the format of the email is correct (e.g., contains an "@" symbol and a domain). This is a validation check.
  2. Verify: Verification is the process of checking if something is true, accurate, or consistent. It's about confirming that the results meet the expected outcomes, often after some processing has taken place.
    • Example: After sending a confirmation email to the address provided during registration, the system waits for the user to click on a verification link. This action verifies that the email address actually belongs to the user and is functional.

 

  • Validation: Ensures data meets certain criteria before processing (e.g., format, length).
  • Verification: Confirms data's accuracy or uniqueness, often after some initial processing (e.g., checking database for duplicates).

 

 

문제상황 

 

UserController

@GetMapping("/check-account-duplicate")
    public ResponseEntity<ApiResponse>checkAccountDuplicate(@RequestParam @Valid  String account) throws IllegalAccessException {
        userService.checkAccountDuplicate(account);
        return ResponseEntity.ok(new ApiResponse("사용 가능한 계정입니다."));
    }

 

UserService

 public void checkAccountDuplicate(String account) {
        Boolean isExist = userRepository.findByUserAccount(account);
        if(isExist){
            throw new IllegalAccessException("중복된 ID 입니다. 새로운 ID를 입력해주세요");
        }
    }

 

 

UserRepository

public interface UserRepository extends JpaRepository <User,Long> {
    User findByUserUUID(UUID uuid);
    Boolean findByUserAccount(String account);

}

 

postman 

 

 

아이디 중복 검사 로직

  • 아이디를 param으로 받아서 userRepository(회원 데이터 저장소)에서 해당 아이디가 존재하는지 확인한다. 
  • 아이디가 존재하는 경우 중복된 아이디  처리를 해준다. 

 

포스트맨 요청 방법

  • @requestParam 으로 요청시에는 url로 전달된 값을 가져오기 때문에 주소 입력란에 직접 작성한다. 
  •  또는 밑에 Params 칸에서 key-value 로 직접 값을 설정해주면 된다. 

 

 

 


 

 

원인 분석 

 

The mismatch is happening because the method findByUserAccount in UserRepository is incorrectly defined to return a Boolean when it should actually be returning a User or checking for the existence in a boolean method.

Method Definition Issue
: The method findByUserAccount is expected to return a Boolean, but it is actually returning a User.

 

 

 userRepository에서 account가 존재하는지 안하는지를 찾을때 리턴값을 boolean으로 설정했다. 

하지만 메서드의 이름이 findByUserAccount 였기 때문에 jpa 메서드 이름 규칙에 따라 (boolean값을 return 하는 것이 아닌)

user을 리턴했던것이다.  그래서 userRepositroy 에서 타입이 불일치 한다는 오류가 발생했던것이다. 

 

내가 원하는대로 boolean값을 리턴하도록 하고싶다면 existsBy ~  를 사용했어야한다. 

 


해결방법

public interface UserRepository extends JpaRepository <User,Long> {
    User findByUserUUID(UUID uuid);
    Boolean existsByUserAccount(String account); 
}

 

Boolean의 리턴 값을 갖도록 메서드의 이름을 수정해줬다. 이후 테스트를 진행하니 잘 수행되었다~ 

 

 

※ 참고

 

https://wisdom-cs.tistory.com/66

 

[Spring Data JPA] 기본 사용법 정리

전에 공부했던 Spring Data JPA의 기본 사용법을 정리하고자 한다. ✔️ Dependency build.gradle 파일의 dependencies 부분에 다음을 추가하자. implementation ‘org.springframework.boot:spring-boot-starter-data-jpa’ ✔️ 공

wisdom-cs.tistory.com

 

 

 


                                                         JPA 리포지토리 메서드 이름 명명시 주의하기 

 

JPA 리포지토리에서 메서드를 작성할때는 리턴 타입이 무엇인지에 따라 메서드 명이 바뀌기 때문에 이에 유의해서 메서드 이름을 작성하자 ( 내 맘대로 이름 작성하는거 아님!! ) 

 

https://velog.io/@soopy368/web-Get%EA%B3%BC-Post%EC%9D%98-%EC%B0%A8%EC%9D%B4%EB%A5%BC-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90

 

[web] Get과 Post의 차이를 알아보자

GET과 POST는 HTTP 메서드로 클라이언트에서 서버에 정보를 요청할 때 사용한다. 사용 목적에 맞게 HTTP 통신을 제대로 사용할 수 있도록 두 메서드의 차이에 대해서 자세히 알아보자.

velog.io

 

get은 단순 조회용, post는 생성 및 업데이트와 같이 db의 값이 변화하는 경우에 사용 

 

 

 

 


API 

 

이메일 인증 후 회원 가입 

// 이메일 인증 확인 후 회원가입
@PostMapping("/verify-email")
public ResponseEntity<ApiResponse>verifyEmail(@RequestParam @Valid  UUID emailTokenId){
    UserTemp userTemp = userService.checkEmailToken(emailTokenId);
    User signUpUser = userService.signUpUser(userTemp);
    ApiResponse response = new ApiResponse( "회원 가입 완료",signUpUser);

    return new ResponseEntity<>(response, HttpStatus.OK);
}

 

이메일 인증을 받은 다음 회원 가입을 처리해주는 메서드인 verifyEmail은 회원을 생성하는 거니까 get X post방식 

문제상황

스케쥴러로 미인증 회원들 삭제기능 만들었는데  임시회원이 인증 받으면 이메일 토큰 테이블이랑 임시회원 테이블에서 데이터 삭제하고 , 미인증 받은 상태로 일정 시간지나면 두 테이블에 있는 데이터들 전부 삭제한다.  근데  테이블에 있는 데이터 들을 전부 삭제한 상태에서 새로운 임시 회원이 들어오면 id값이 1 부터 시작되는게 아니라 이전 id값 다음값 부터 시작하게 된다.  이런 경우 데이터가 쌓이면 쌓일수록 계속 id값이 계속 커지기만 한다.  이때 db의 성능과 관련한 문제가 생기지 않을까?  계속해서 id값이 커지더라도 상관 없을까? 

 

 

테이블이 전부 삭제된 후에 새로운 회원이 들어왔는데 이전 회원의 id값 다음값이 생성된다면 그 앞에 있는 id값들이 낭비 되는거 아닐까? 

예를 들어서 100명이 임시 회원 가입을 했다가 인증을 받아서 회원가입해서 삭제되어 테이블이 전부 비워진 상태에서 새로운 데이터가 생성되는데  id값이 101부터 시작하면 1~100의 값은 사용되지 못하는 거니까 테이블 낭비 아닐까?? 

 

 

원인 분석 

@Table(name = "USERTEMP_TABLE")
public class UserTemp {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="USERTEMP_ID")
    private Long userTempId;
  }

 

현재 내 userTemp 테이블의 pk는 자동으로 값이 증가하도록 설정해놨다. 

이 설정은 데이터베이스가 자동 증가된 값을 관리하도록 하고, 삭제된 레코드의 ID를 재사용하지 않는다.

그래서 삭제된 후 새로운 레코드를 삽입하면 연속되지 않는 ID를 얻게 되는 것이다. 

 

해결 방법 

id값이 계속해서 커지거나 연속적이지 않은 id값이 만들어진다고 해서 성능에 큰 차이가 발생하지는 않는다고 한다. 

오히려 연속적인 id 값을 만들기 위해 아래와 같은 설정을 했다가 트랜잭션과 데이터 무결성 부분에서 문제가 발생할 수 있다 

 

굳이 연속적으로 id값을 생성해야하는 이유가 아니라면 다른 설정을 해줄 필요는 없을 것 같다. 

 

TRUNCATE TABLE user_temp RESTART IDENTITY;
ALTER SEQUENCE user_temp_seq RESTART WITH 1;

 

참고자료 

 

https://transferhwang.tistory.com/112

 

[Spring Boot] h2 Database 인덱스 auto_increment 초기화

스프링 부트 공부를 하면서 h2 DB를 이용해 실습하던 도중 다음과 같은 문제를 만났습니다. 임의의 DB의 테이블에 인덱스 값이 1,2,3,4 인 4개의 레코드가 있다고 가정했을때, 4번 인덱스의 레코드를

transferhwang.tistory.com

 

https://stackoverflow.com/questions/10065386/resetting-autoincrement-in-h2

 

Resetting autoincrement in h2

I'm testing a controller that returns a json response but the tests fail after the first time because the h2 database does not reset the auto increment id. Using fixtures or creating objects manual...

stackoverflow.com

 

 

 

+ Recent posts