Skip to content

[feat] 캡슐 CRUD / IPFS 연동#25

Open
si-zero wants to merge 7 commits intomainfrom
feat/#18
Open

[feat] 캡슐 CRUD / IPFS 연동#25
si-zero wants to merge 7 commits intomainfrom
feat/#18

Conversation

@si-zero
Copy link
Copy Markdown
Member

@si-zero si-zero commented Jul 20, 2025

😺 Issue

✅ 작업 리스트

  • 캡슐 CRUD 구현
  • 스웨거 연동
  • IPFS 연동
  • 전역 에러

⚙️ 작업 내용

  • 캡슐 CRUD 구현

    • 캡슐을 엔티티, 레포지토리, 컨트롤러, 서비스, Dto로 계층을 나눠 구현
    • 이미지/동영상 또한, CID로 저장
  • 스웨거 연동

    • 스웨거 UI를 통한 빠른 테스트 가능토록 설정
    • UI로 시각적으로 확인 가능
  • IPFS 연동

  • 전역 에러

📷 테스트 / 구현 내용

  • 캡슐 등록
image
  • 이미지 등록 확인
image

🤔 문제점 / 궁금한 점 (필요 시)

  • 만들면서도 지금 뭘 만드는건지 정확하게 모르겠는데, 혹시 나중에 강의 좀 들을 수 있을까요;

📁 참고 자료

@si-zero si-zero self-assigned this Jul 20, 2025
@si-zero si-zero added ⚙️ setting 프로젝트 환경설정 / Settings for project 👾 feat 새 기능 / New features 📦 chore 세세한 변경사항 / tiny changes 🐵 시영 labels Jul 20, 2025

Capsule saved = capsuleService.createCapsule(dto, cid);
return ResponseEntity.status(HttpStatus.CREATED).body(saved);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PostMapping
public ResponseEntity createCapsule(@ModelAttribute InputDto dto);

InputDto {
MultipartFile meiaFile;
String data;
}

@ModelAttribute annotion을 사용해 String과 MultipartFile 동시에 받을 수 있음

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ModelAttribute 라는 어노테이션 사용하여 컨트롤러 코드 구조 변경해봤습니다 감사합니당.

@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseEntity<CapsuleDto.Select> createCapsule(@ModelAttribute CapsuleDto.Create requestDto) {
        CapsuleDto.Select created = capsuleService.createCapsule(requestDto);
        return ResponseEntity.status(HttpStatus.CREATED).body(created);
    }

구조 변경하면서 Dto도 기존에는 기능별로 나뉘어져 있었는데 구조 합쳐서 다시 만들었습니다

public class CapsuleDto {

    // 캡슐 생성
    @Getter @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Create {
        private String capsuleType;
        private String title;
        private String content;
        private Date openTime;

        // 이미지 & 동영상
        private MultipartFile mediaFile;
    }

    // 캡슐 조회
    @Getter @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Select {
        private Long casuleId;
        private String capsuleType;
        private String title;
        private String content;
        private Date openTime;
    }

    // 캡슐 수정
    @Getter @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Update {
        private String capsuleType;
        private String title;
        private String content;
        private Date openTime;
    }
}

이런식으로 변경했는데, 어노테이션이 겹치는 부분이 너무 많은데 괜찮은거 맞을까용;;;
리뷰 감사합니다. 배워갑니당 >0<

@Getter
@Setter
public class CapsuleCreateRequestDto {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dto class의 경우
spring boot에 의해 json parsing되는 일이 많음으로
@NoArgStructure을 꼭 붙여준다 (없을시 기본 생성되지만 명시하는게 좋음)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 부분 반영하여 코드 수정하겠습니다. 감사합니다 :)


public class CapsuleSelectResponseDto {

@Getter
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Getter는 dto column에 두지 말고
class위에 둘것

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앗; 알겠습니다. 바로 반영하겠습니다. 감사합니당


// IPFS CID 필드 ( 이미지 / 동영상 )
private String mediaCid;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

column 마다
@column annotion붙이기

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


// 캡슐 아이디
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long capsuleId;

  // 캡슐 종류
  @Column(nullable = false)
  private String capsuleType;

  // 제목
  @Column(nullable = false)
  private String title;

  // 내용
  @Column
  private String content;

  // 캡슐 오픈일
  @Column
  private Date openTime;

  // IPFS CID 필드 ( 이미지 / 동영상 )
  @Column
  private String mediaCid;

엔티티 구조도 @column 사용하여 변경했습니당. 감사합니다 :):)


return capsuleRepository.save(capsule);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createCapule 함수의 위치가 올바르지 않음

  1. 확장중 다른 service에서 CapuleService에 의존성을 가진다면 createCapules함수에도 의존성을 가지게 됨
  2. dto -> entity 함수는 대부분 dto에 의존성을 가짐으로
    dto로 옮기는게 좋음

@Getter
@Setter
@NoArgStructor
class Dto {
int id; String col;
public Entity toEntity() { return Entity.builder(). .. .build(); } // dto -> entity 함수
public static Dto of(Entity entity) { return new Dto(); } // entity -> dto 함수
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 아직 완벽하게 이해를 못해서 좀 더 알아보고 수정하여 다시 코멘트 올리겠습니다!

return ResponseEntity.ok(cid);
} catch (Exception e) {
return ResponseEntity.internalServerError().body("IPFS 파일 업로드 실패: " + e.getMessage());
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

catch (Exception e) { ... }는 다른 모든 exception또한 처리해버림 (만약 다른 부분에서 exception이 발생해도 무조건 "파일 업로드 실패" 라는 404를 반환하게됨)
때문에
IpfsService :: 27줄에서 throw 한 exception을 CustomIpfsException으로 class를 생성해서 던지고
catch(CustomIpfsException e) { ... } 으로 처리하는게 맞음

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 전역 에러 처리도 다시 한 번 공부해서 수정하겠습니다.


public IpfsService() {
this.ipfs = new IPFS("/ip4/127.0.0.1/tcp/5001"); // IPFS 데몬 주소
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IpfsConfig 에서 이미 @bean으로 등록했음 새로 생성하지 말고 주입을 받아 사용하면 됨

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bean 어노테이션도 한 번 더 찾아보고 수정하겠습니다. 감사합니다!.!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐵 시영 📦 chore 세세한 변경사항 / tiny changes 👾 feat 새 기능 / New features ⚙️ setting 프로젝트 환경설정 / Settings for project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants