TACO 프로젝트 회고록(2023-05-16)

윤현우·2023년 5월 17일
0

TACO 프로젝트 회고록

목록 보기
18/31
post-thumbnail

오늘은 사용자 프로필 저장 기능을 구현해보았다.

엔티티

사용자는 하나의 프로필을 설정할 수 있기 때문에 1대1관계라 생각하고 ProfileEntity를 따로 하나 더 생성하였다.

ProfileEntity

public class ProfileEntity {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long profileIndex;

    @Column
    private Long userIndex;

    @Column
    private String originFileName;

    @Column
    private String storeFileName;

    public ProfileDto toDto(){
        ProfileDto profileDto = ProfileDto.builder()
        .userIndex(userIndex)
        .originFileName(originFileName)
        .storeFileName(storeFileName)
        .build();

        return profileDto;
    }
}

private String originFileName;

  • 사용자가 업로드한 원본 이름

private String storeFileName;

  • 업로드된 파일의 이름이 겹칠 수 있기 때문에 저장한 파일 이름을 다르게 했다.

private Long userIndex;

  • 사용자 인덱스의 FK

FileStore Class

파일을 저장하는데 있어서 특화된 메소드들을 따로 클래스로 구성하여 이를 분리해두었다.

FileStore

public class FileStore {

    @Autowired
    private ProfileRepository profileRepository;
    
    // 파일 경로
    @Value("${file.dir}")
    private String fileDir;

    // 파일 경로 구성
    public String getFullPath(String storeFileName) {
        return fileDir + storeFileName;
    }

    // 프로필을 저장 -  이미 유저인덱스로 찾아 엔티티가 있을 경우 수정 / 없으면 저장
    public Optional<ProfileEntity> storeFile(MultipartFile multipartFile, Long userIndex) throws IOException {
        if(multipartFile.isEmpty()){
            return null;
        }
        
        String originFileName = multipartFile.getOriginalFilename();
        String storeFileName = createStoreFileName(originFileName);
        multipartFile.transferTo(new File(getFullPath(storeFileName)));

        ProfileEntity entity = ProfileEntity.builder()
                                    .originFileName(originFileName)
                                    .storeFileName(storeFileName)
                                    .userIndex(userIndex)
                                    .build();

        Optional<ProfileEntity> profileEntity = profileRepository.findByUserIndex(userIndex);
        
        // 만약 유저 인덱스를 가진 프로필엔티티가 있으면 해당 프로필 엔티티를 수정
        if(profileEntity != null){
            profileEntity.map(p -> {
                profileEntity.get().setOriginFileName(originFileName);
                profileEntity.get().setStoreFileName(storeFileName);
                profileEntity.get().setUserIndex(userIndex);

                return p;
            })
            .map(p -> profileRepository.save(p));

            return profileEntity;
        }
        // 없다면 프로필 엔티티를 생성해서 저장
        else{
            ProfileEntity saved = profileRepository.save(entity);
            Optional<ProfileEntity> find =profileRepository.findById(userIndex);
            return find;
        }
    }
    
    // storeFile 이름 구성
    private String createStoreFileName(String originFileName) {
        String uuid = UUID.randomUUID().toString();
        String ext = extractExt(originFileName);
        String storeFileName = uuid + ext;

        return storeFileName;
    }

    // 확장자 추출
    private String extractExt(String originFileName) {
        int idx = originFileName.lastIndexOf(".");
        String ext = originFileName.substring(idx);
        return ext;
    }
}

@Value("${file.dir}")
private String fileDir;

  • 파일을 실제로 저장해둘 경로를 지정

private String extractExt(String originFileName){...}

  • 파일의 확장자를 추출하는 메서드

private String createStoreFileName(String originFileName) {...}

  • 저장할 파일 이름 구성
  • UUID를 통해 동일한 파일이름 저장 방지

public String getFullPath(String storeFileName) {...}

  • storeFileName에 들어갈 파일 경로 구성

public Optional<ProfileEntity> storeFile(MultipartFile multipartFile, Long userIndex) throws IOException {...}

  • 원본 파일 이름과 저장할 파일 이름을 반환하게 해주는 메서드

이 storeFile 메서드가 가장 중요한 부분이다.

파일을 저장시켜주는 메서드로써, originFileName과 storeFileName의 값을 반환해야 한다.

파라미터를 multipartFile과 회원정보를 수정하고 있는 유저의 인덱스를 받아와

multipartFile들을 String타입으로 변환 후 새로운 ProfileEntity를 생성한다.

하지만, 프로필은 하나만 있어야 하므로, 만약 동일한 userIndex를 갖고 있는 ProfileEntity가 있는 경우 해당 Entity에 덮어씌우게 하고, 동일한 userIndex가 없다면 Entity를 생성하는 방식으로 만들었다.


컨트롤러

프로필 변경 작업은 회원정보 변경 페이지에서 진행되기 때문에 UserController에 로직을 생성하였다.

이전에 프로필이 아닌 이름, 닉네임, 전화번호를 변경하는 로직에 프로필 변경 로직을 추가하였다.

    // 회원정보 수정 버튼 기능
    @PostMapping("/user/edit")
    public ResponseEntity<Optional<UserEntity>> userEdit(HttpSession session,@RequestParam("originFileName") MultipartFile originFileName, @ModelAttribute UserDto userDto) throws IOException{
        Long userIndex = (Long) session.getAttribute("userIndex");
        
        UserEntity user = userDto.toEntity();

        // 회원 정보 수정
        Optional<UserEntity> userInfo = userService.changeUserInfo(userIndex, user);

        // 프로필 수정
        profileService.saveProfile(originFileName, userIndex);

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

Optional<UserEntity> userInfo = userService.changeUserInfo(userIndex, user);

  • 해당 페이지의 세션을 받아와 사용자의 userIndex를 받아오고, 수정된 이름, 닉네임, 전화번호를 dto로 받아와 수정시켜준다.

여기까지는 이전에 있던 로직이다.

profileService.saveProfile(originFileName, userIndex);

  • 프로필이 변경된 파일을 MultipartFile로 받아와 회원정보 수정 메서드와 같이 userIndex와 함께 서비스 부분으로 넘겨준다.

서비스

    public ProfileEntity saveProfile(MultipartFile originFileName, Long userIndex) throws IOException {
        fileStore.storeFile(originFileName, userIndex);
        return null;
    }

컨트롤러에서 받아온 파일과 userIndex를 StoreFile메서드로 넘겨 ProfileEntity를 생성하고, 파일을 저장한다.


아직 로직을 전부 만들지는 않았지만, 파일과 이미지 업로드 기능에 대하여 여러가지 공부를 하였다.

이 이미지 저장 로직에 대해 이해가 잘 안되었어서 오래걸리긴 했지만, 혼자 공부하는 것 치고 꽤 빠르게 이해한 것 같다.

또 이렇게 기술 회고를 작성하며 복습할 수 있어서 참 다행인것 같다.

만약 복습을 하지 않았다면, 이 프로젝트가 끝난 뒤에 나중에 또 이미지 파일을 저장할 때 다시 공부를 해야겠지만, 복습을 통해서 완전히 나의 것으로 만들었다.

오늘은 안풀리던 이미지 저장 로직을 이해해서 참 기분 좋았다.

앞으로 남은 로직과 프론트와의 데이터 렌더링을 잘 마무리 하고, 프로젝트를 성공적으로 끝낼 수 있었으면 좋겠다.

profile
개발자가 되는 그날까지

0개의 댓글