반응형

이번에 프로젝트를 하면서 File 테이블의 ID는 Long, Integer로 사용하는 것이 아닌 KeyManager라는 테이블을 참조해서 FI + (오늘의 날짜) + 인덱스 이렇게 문자열로 생성해서 넣어주게 되었다.

 

package NexaDs_Knu.practice.Data.Entity.Util;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Embeddable;
import java.io.Serializable;
import java.time.LocalDateTime;

@Embeddable
@Data
@AllArgsConstructor
@NoArgsConstructor
public class KeyManagerId implements Serializable {

    private String keyType;
    private String date;
}

이렇게 ID는 문자열로 타입과, 문자열로 변환된 날짜를 넣어준 KeyManagerId를 사용하고

package NexaDs_Knu.practice.Data.Entity;

import NexaDs_Knu.practice.Data.Entity.Util.KeyManagerId;
import lombok.*;

import javax.persistence.*;

@Entity
@Table(name = "KeyManager")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class KeyManagerEntity {

    @EmbeddedId
    private KeyManagerId id;


    private Long sequence;
}

이렇게 테이블을 작성하였다.

이렇게 되면 ID는 KeyType과 날짜가 되어 이 두가지가 중복된 데이터는 들어올 수 없게 되었다.

@Query("SELECT k FROM KeyManagerEntity k WHERE k.id.keyType = :keyType AND k.id.date = :date")
KeyManagerEntity findByKeyTypeAndDate(
	@RequestParam("keyType") String keyType,
    @RequestParam("date") String date);
)

이렇게 JPQL을 이용하여 KeyType과 Date에 맞는 KeyManagerEntity를 가져오고 

	public static String toPrimaryKeyId(String keyType){
        String today = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        KeyManagerEntity keyManager = keyManagerRepository.findByKeyTypeAndDate(keyType, today);
        Long sequence;
        if(keyManager == null)
        {
            keyManager = new KeyManagerEntity(new KeyManagerId(keyType, today), 1L);
            keyManagerRepository.save(keyManager);
            sequence = 1L;
        }
        else
        {
            keyManager.setSequence(keyManager.getSequence() + 1);
            sequence = keyManager.getSequence();
            keyManagerRepository.save(keyManager);
        }
        return keyType + today + String.format("%05d", sequence);
    }

이렇게 그에 따라 ID를 생성해주게 만들었다.

 

그리고 이 방법을 사용하던 도중에 에러가 발생했다.

 

자바스크립트에서 해당 방법을 사용하는 API를 반복문으로 실행하는 구문이 있었는데, 이 과정에서 

이런 에러가 발생한 것이다.

현재 toPrimaryKeyId에서 동일한 ID를 생성해주고 있는 것이었다.

 

왜 이런 일이 생겼는지 생각해보면, 스프링의 쓰레드에서 동시에 메서드에 접근하고 +1된 값을 넣어주기 전에 같은 ID를 만들어 버린 이유일 것이다.

 

어찌보면 당연한 일이었다.

이 부분을 critical section으로 만들었어야 했는데, '어차피 자바스크립트 반복문으로 들어오니 대충 순서대로 들어오겠지' 라고 생각한 나의 잘못일 것이다.

 

해결 방법은 이 부분을 atomic하게 바꿔주는 어떤 방법이든 될 수 있겠지만, 나는 가장 간단한 @Synchronized 어노테이션을 달아주었다.

이렇게만 바꿔주었더니, 더 이상 에러가 발생하지 않았다.

스프링이 쓰레드 기반으로 동작된다는 것을 다시 한 번 느낄 수 있는 에러였다.

 

 

'현장실습' 카테고리의 다른 글

Mariadb와 Springboot를 Docker에 올리기  (0) 2023.07.28

+ Recent posts