From ade1bc7243dd1c4162cf7bde7f18c18a702b3b64 Mon Sep 17 00:00:00 2001
From: ji hwan <jjhinu1004@gmail.com>
Date: Thu, 18 Apr 2024 18:44:18 +0900
Subject: [PATCH] =?UTF-8?q?#13=20feat:=20=EB=AA=A8=EC=9E=84=20=EA=B4=80?=
 =?UTF-8?q?=EB=A0=A8=20api=EA=B5=AC=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../auth/domain/PrincipalDetails.java         |   3 +
 .../auth/jwt/JwtAuthenticationFilter.java     |   5 +-
 .../capstone/auth/jwt/JwtProvider.java        |  13 +-
 .../capstone/auth/service/AuthService.java    |   2 +-
 .../auth/service/PrincipalDetailService.java  |   8 +-
 .../club/controller/ClubController.java       |  73 +++++++++++
 .../controller/dto/ClubCreateRequest.java     |  12 ++
 .../club/controller/dto/ClubResponse.java     |  20 +++
 .../project/capstone/club/domain/Club.java    |  10 +-
 .../capstone/club/domain/ClubRepository.java  |  18 +++
 .../capstone/club/domain/PublicStatus.java    |  25 ++++
 .../capstone/club/service/ClubService.java    | 119 ++++++++++++++++++
 .../capstone/common/domain/MemberClub.java    |   7 ++
 .../common/domain/MemberClubRepository.java   |  12 ++
 .../capstone/config/SecurityConfig.java       |   2 +-
 .../capstone/member/domain/Member.java        |   3 +-
 .../member/domain/MemberRepository.java       |   1 +
 backend/src/main/resources/application.yml    |   5 +
 backend/src/main/resources/data.sql           |  43 +++++++
 19 files changed, 364 insertions(+), 17 deletions(-)
 create mode 100644 backend/src/main/java/com/project/capstone/club/controller/ClubController.java
 create mode 100644 backend/src/main/java/com/project/capstone/club/controller/dto/ClubCreateRequest.java
 create mode 100644 backend/src/main/java/com/project/capstone/club/controller/dto/ClubResponse.java
 create mode 100644 backend/src/main/java/com/project/capstone/club/domain/ClubRepository.java
 create mode 100644 backend/src/main/java/com/project/capstone/club/domain/PublicStatus.java
 create mode 100644 backend/src/main/java/com/project/capstone/club/service/ClubService.java
 create mode 100644 backend/src/main/java/com/project/capstone/common/domain/MemberClubRepository.java
 create mode 100644 backend/src/main/resources/data.sql

diff --git a/backend/src/main/java/com/project/capstone/auth/domain/PrincipalDetails.java b/backend/src/main/java/com/project/capstone/auth/domain/PrincipalDetails.java
index 9c03357b1d..6e9f9292b7 100644
--- a/backend/src/main/java/com/project/capstone/auth/domain/PrincipalDetails.java
+++ b/backend/src/main/java/com/project/capstone/auth/domain/PrincipalDetails.java
@@ -6,6 +6,7 @@
 import org.springframework.security.core.userdetails.UserDetails;
 
 import java.util.Collection;
+import java.util.UUID;
 
 @AllArgsConstructor
 public class PrincipalDetails implements UserDetails {
@@ -27,6 +28,8 @@ public String getUsername() {
         return member.getEmail();
     }
 
+    public String getUserId() {return member.getId().toString(); }
+
     @Override
     public boolean isAccountNonExpired() {
         return true;
diff --git a/backend/src/main/java/com/project/capstone/auth/jwt/JwtAuthenticationFilter.java b/backend/src/main/java/com/project/capstone/auth/jwt/JwtAuthenticationFilter.java
index fde6025684..99e9e0c140 100644
--- a/backend/src/main/java/com/project/capstone/auth/jwt/JwtAuthenticationFilter.java
+++ b/backend/src/main/java/com/project/capstone/auth/jwt/JwtAuthenticationFilter.java
@@ -25,8 +25,9 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
         String token = jwtProvider.resolveToken(request);
         try {
-            String email = jwtProvider.validateTokenAndGetEmail(token);
-            Authentication authentication = jwtProvider.createAuthentication(email);
+            String id = jwtProvider.validateTokenAndGetId(token);
+            Authentication authentication = jwtProvider.createAuthentication(id);
+            log.info(authentication.getName());
             SecurityContextHolder.getContext().setAuthentication(authentication);
         } catch (Exception e) {
             log.warn(e.getMessage());
diff --git a/backend/src/main/java/com/project/capstone/auth/jwt/JwtProvider.java b/backend/src/main/java/com/project/capstone/auth/jwt/JwtProvider.java
index 69e8c321ab..f04072efb1 100644
--- a/backend/src/main/java/com/project/capstone/auth/jwt/JwtProvider.java
+++ b/backend/src/main/java/com/project/capstone/auth/jwt/JwtProvider.java
@@ -20,6 +20,7 @@
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.util.Date;
+import java.util.UUID;
 
 @Slf4j
 @Component
@@ -39,9 +40,9 @@ private void init() {
         key = Keys.hmacShaKeyFor(secret.getBytes());
     }
 
-    public String generate(String email) {
+    public String generate(String id) {
         Claims claims = Jwts.claims();
-        claims.put("email", email);
+        claims.put("id", id);
         return generateToken(claims);
     }
 
@@ -74,17 +75,17 @@ public String resolveToken(HttpServletRequest request) {
         return null;
     }
 
-    public String validateTokenAndGetEmail(String token) {
+    public String validateTokenAndGetId(String token) {
         return Jwts.parserBuilder()
                 .setSigningKey(key)
                 .build()
                 .parseClaimsJws(token)
                 .getBody()
-                .get("email", String.class);
+                .get("id", String.class);
     }
 
-    public Authentication createAuthentication(String email) {
-        UserDetails userDetails = principalDetailService.loadUserByUsername(email);
+    public Authentication createAuthentication(String id) {
+        UserDetails userDetails = principalDetailService.loadUserByUsername(id);
         return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
     }
 }
diff --git a/backend/src/main/java/com/project/capstone/auth/service/AuthService.java b/backend/src/main/java/com/project/capstone/auth/service/AuthService.java
index 621dc977a2..cd48022f04 100644
--- a/backend/src/main/java/com/project/capstone/auth/service/AuthService.java
+++ b/backend/src/main/java/com/project/capstone/auth/service/AuthService.java
@@ -29,6 +29,6 @@ public TokenResponse login(String email) {
     }
 
     private String generateToken(Member member) {
-        return jwtProvider.generate(member.getEmail());
+        return jwtProvider.generate(member.getId().toString());
     }
 }
diff --git a/backend/src/main/java/com/project/capstone/auth/service/PrincipalDetailService.java b/backend/src/main/java/com/project/capstone/auth/service/PrincipalDetailService.java
index 3ea1d0cc44..ef31fbb814 100644
--- a/backend/src/main/java/com/project/capstone/auth/service/PrincipalDetailService.java
+++ b/backend/src/main/java/com/project/capstone/auth/service/PrincipalDetailService.java
@@ -9,14 +9,16 @@
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.stereotype.Service;
 
+import java.util.UUID;
+
 @Service
 @RequiredArgsConstructor
 public class PrincipalDetailService implements UserDetailsService {
     private final MemberRepository memberRepository;
     @Override
-    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
-        Member member = memberRepository.findMemberByEmail(email)
-                .orElseThrow(() -> new UsernameNotFoundException("이메일이 존재하지 않습니다."));
+    public UserDetails loadUserByUsername(String id) throws UsernameNotFoundException {
+        Member member = memberRepository.findMemberById(UUID.fromString(id))
+                .orElseThrow(() -> new UsernameNotFoundException("해당 멤버가 존재하지 않습니다."));
         return new PrincipalDetails(member);
     }
 }
diff --git a/backend/src/main/java/com/project/capstone/club/controller/ClubController.java b/backend/src/main/java/com/project/capstone/club/controller/ClubController.java
new file mode 100644
index 0000000000..508063a297
--- /dev/null
+++ b/backend/src/main/java/com/project/capstone/club/controller/ClubController.java
@@ -0,0 +1,73 @@
+package com.project.capstone.club.controller;
+
+import com.project.capstone.auth.domain.PrincipalDetails;
+import com.project.capstone.club.controller.dto.ClubCreateRequest;
+import com.project.capstone.club.controller.dto.ClubResponse;
+import com.project.capstone.club.service.ClubService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.UUID;
+
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/club")
+@Slf4j
+public class ClubController {
+    private final ClubService clubService;
+    // 모임 불러오기
+    @GetMapping("/search/{id}")
+    public ResponseEntity<?> getClub(@PathVariable Long id) {
+        ClubResponse clubResponse = clubService.getClub(id);
+        return ResponseEntity.ok().body(clubResponse);
+    }
+
+    // 모임 주제별 검색
+    @GetMapping("/search/topic")
+    public ResponseEntity<List<ClubResponse>> searchByTopic(@RequestParam String topic) {
+        List<ClubResponse> clubResponseList = clubService.searchByTopic(topic);
+        return ResponseEntity.ok().body(clubResponseList);
+    }
+    // 모임 이름으로 검색
+    @GetMapping("/search/name")
+    public ResponseEntity<?> searchByName(@RequestParam String name) {
+        List<ClubResponse> clubResponseList = clubService.searchByName(name);
+        return ResponseEntity.ok().body(clubResponseList);
+    }
+    // 모임 생성하기
+    @PostMapping("/create")
+    public ResponseEntity<?> createClub(@RequestBody ClubCreateRequest request, @AuthenticationPrincipal PrincipalDetails details) {
+        clubService.createClub(request, details.getUserId());
+        return ResponseEntity.ok().body("모임 생성 완료");
+    }
+    // 모임 가입하기
+    @GetMapping("/join")
+    public ResponseEntity<?> join(@AuthenticationPrincipal PrincipalDetails details, @RequestParam Long clubId) {
+        clubService.join(details.getUserId(), clubId);
+        return ResponseEntity.ok().body("모임 가입 완료");
+    }
+    // 모임 탈퇴하기
+    @GetMapping("/out")
+    public ResponseEntity<?> out(@AuthenticationPrincipal PrincipalDetails details, @RequestParam Long clubId) {
+        clubService.out(details.getUserId(), clubId);
+        return ResponseEntity.ok().body("모임 탈퇴 완료");
+    }
+    // 모임장 위임하기
+    @PutMapping("/delegate")
+    public ResponseEntity<?> delegateManager(@AuthenticationPrincipal PrincipalDetails details,
+                                             @RequestParam UUID memberId, @RequestParam Long clubId) {
+        clubService.delegateManager(details, memberId, clubId);
+        return ResponseEntity.ok().body("위임 완료");
+    }
+    // 멤버 추방하기
+    @GetMapping("/expel")
+    public ResponseEntity<?> expelMember(@AuthenticationPrincipal PrincipalDetails details,
+                                         @RequestParam UUID memberId, @RequestParam Long clubId) {
+        clubService.expelMember(details, memberId, clubId);
+        return ResponseEntity.ok().body("추방 완료");
+    }
+}
diff --git a/backend/src/main/java/com/project/capstone/club/controller/dto/ClubCreateRequest.java b/backend/src/main/java/com/project/capstone/club/controller/dto/ClubCreateRequest.java
new file mode 100644
index 0000000000..0f556be5ed
--- /dev/null
+++ b/backend/src/main/java/com/project/capstone/club/controller/dto/ClubCreateRequest.java
@@ -0,0 +1,12 @@
+package com.project.capstone.club.controller.dto;
+
+import com.project.capstone.club.domain.PublicStatus;
+
+public record ClubCreateRequest(
+        String topic,
+        String name,
+        int maximum,
+        PublicStatus publicStatus,
+        Integer password
+) {
+}
diff --git a/backend/src/main/java/com/project/capstone/club/controller/dto/ClubResponse.java b/backend/src/main/java/com/project/capstone/club/controller/dto/ClubResponse.java
new file mode 100644
index 0000000000..c47b916330
--- /dev/null
+++ b/backend/src/main/java/com/project/capstone/club/controller/dto/ClubResponse.java
@@ -0,0 +1,20 @@
+package com.project.capstone.club.controller.dto;
+
+import com.project.capstone.club.domain.Club;
+import com.project.capstone.club.domain.PublicStatus;
+
+import java.time.LocalDateTime;
+
+public record ClubResponse (
+        Long id,
+        Long bookId,
+        String topic,
+        String name,
+        LocalDateTime createdAt,
+        int maximum,
+        PublicStatus publicstatus
+) {
+    public ClubResponse(Club club) {
+        this(club.getId(), club.getBook() == null ? null : club.getBook().getId(), club.getTopic(), club.getName(), club.getCreatedAt(), club.getMaximum(), club.getPublicStatus());
+    }
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/project/capstone/club/domain/Club.java b/backend/src/main/java/com/project/capstone/club/domain/Club.java
index c1b31f1799..e64191347a 100644
--- a/backend/src/main/java/com/project/capstone/club/domain/Club.java
+++ b/backend/src/main/java/com/project/capstone/club/domain/Club.java
@@ -1,6 +1,7 @@
 package com.project.capstone.club.domain;
 
 import com.project.capstone.book.domain.Book;
+import com.project.capstone.club.controller.dto.ClubCreateRequest;
 import com.project.capstone.common.domain.MemberClub;
 import com.project.capstone.content.domain.Content;
 import com.project.capstone.member.domain.Member;
@@ -16,6 +17,7 @@
 import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.UUID;
 
 @Entity
 @EntityListeners(AuditingEntityListener.class)
@@ -27,14 +29,17 @@ public class Club {
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
+    @Column(name = "manager_id", columnDefinition = "BINARY(16)")
+    private UUID managerId;
     private String topic;
     private String name;
     @Column(name = "created_at")
     @CreatedDate
     private LocalDateTime createdAt;
     private int maximum;
-    @Column(name = "is_public")
-    private boolean isPublic;
+    @Enumerated(EnumType.STRING)
+    @Column(name = "public_status")
+    private PublicStatus publicStatus;
     private Integer password;
 
     @OneToMany(mappedBy = "club")
@@ -48,4 +53,5 @@ public class Club {
 
     @ManyToOne
     private Book book;
+
 }
diff --git a/backend/src/main/java/com/project/capstone/club/domain/ClubRepository.java b/backend/src/main/java/com/project/capstone/club/domain/ClubRepository.java
new file mode 100644
index 0000000000..56c1815329
--- /dev/null
+++ b/backend/src/main/java/com/project/capstone/club/domain/ClubRepository.java
@@ -0,0 +1,18 @@
+package com.project.capstone.club.domain;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+public interface ClubRepository extends JpaRepository<Club, Long> {
+    List<Club> findClubsByTopic(String topic);
+    List<Club> findClubsByNameContaining(String name);
+    Optional<Club> findClubById(Long id);
+    @Modifying(clearAutomatically = true)
+    @Query("update Club c set c.managerId = :id")
+    void updateManager(UUID id);
+}
diff --git a/backend/src/main/java/com/project/capstone/club/domain/PublicStatus.java b/backend/src/main/java/com/project/capstone/club/domain/PublicStatus.java
new file mode 100644
index 0000000000..4e47ba4bf5
--- /dev/null
+++ b/backend/src/main/java/com/project/capstone/club/domain/PublicStatus.java
@@ -0,0 +1,25 @@
+package com.project.capstone.club.domain;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum PublicStatus {
+    PUBLIC("공개"),
+    PRIVATE("비공개");
+
+    private final String description;
+
+
+    @JsonCreator
+    public static PublicStatus from(String status) {
+        for (PublicStatus publicStatus : PublicStatus.values()) {
+            if (publicStatus.getDescription().equals(status)) {
+                return publicStatus;
+            }
+        }
+        throw new RuntimeException("잘못된 공개 여부입니다.");
+    }
+}
diff --git a/backend/src/main/java/com/project/capstone/club/service/ClubService.java b/backend/src/main/java/com/project/capstone/club/service/ClubService.java
new file mode 100644
index 0000000000..28357d919b
--- /dev/null
+++ b/backend/src/main/java/com/project/capstone/club/service/ClubService.java
@@ -0,0 +1,119 @@
+package com.project.capstone.club.service;
+
+import com.project.capstone.auth.domain.PrincipalDetails;
+import com.project.capstone.club.controller.dto.ClubCreateRequest;
+import com.project.capstone.club.controller.dto.ClubResponse;
+import com.project.capstone.club.domain.Club;
+import com.project.capstone.club.domain.ClubRepository;
+import com.project.capstone.common.domain.MemberClub;
+import com.project.capstone.common.domain.MemberClubRepository;
+import com.project.capstone.member.domain.Member;
+import com.project.capstone.member.domain.MemberRepository;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class ClubService {
+    private final ClubRepository clubRepository;
+    private final MemberClubRepository memberClubRepository;
+    private final MemberRepository memberRepository;
+
+    public List<ClubResponse> searchByTopic(String topic) {
+        List<ClubResponse> clubResponseList = new ArrayList<>();
+        log.info(topic);
+        List<Club> clubList = clubRepository.findClubsByTopic(topic);
+        for (Club club : clubList) {
+            clubResponseList.add(new ClubResponse(club));
+        }
+        return clubResponseList;
+    }
+    public List<ClubResponse> searchByName(String name) {
+        List<ClubResponse> clubResponseList = new ArrayList<>();
+        List<Club> clubList = clubRepository.findClubsByNameContaining(name);
+        for (Club club : clubList) {
+            clubResponseList.add(new ClubResponse(club));
+        }
+        return clubResponseList;
+    }
+
+    @Transactional
+    public void createClub(ClubCreateRequest request, String memberId) {
+        Club savedClub = clubRepository.save(Club.builder()
+                .managerId(UUID.fromString(memberId))
+                .topic(request.topic())
+                .name(request.name())
+                .maximum(request.maximum())
+                .publicStatus(request.publicStatus())
+                .password(request.password())
+                .build());
+
+        join(memberId, savedClub.getId());
+    }
+
+    public void join(String memberId, Long clubId) {
+        Member member = memberRepository.findMemberById(UUID.fromString(memberId)).orElseThrow(
+                () -> new RuntimeException("존재하지 않는 멤버입니다.")
+        );
+        Club club = clubRepository.findClubById(clubId).orElseThrow(
+                () -> new RuntimeException("존재하지 않는 모임입니다.")
+        );
+        if (memberClubRepository.findMemberClubByMember_IdAndClub_Id(UUID.fromString(memberId), clubId).isPresent()) {
+            throw new RuntimeException("이미 가입된 모임입니다.");
+        }
+        memberClubRepository.save(new MemberClub(null, member, club));
+    }
+
+    @Transactional
+    public void out(String userId, Long clubId) {
+        Club club = clubRepository.findClubById(clubId).orElseThrow(
+                () -> new RuntimeException("존재하지 않는 모임입니다.")
+        );
+        if (club.getManagerId().toString().equals(userId)) {
+            throw new RuntimeException("모임장은 모임을 나갈 수 없습니다. 모임장을 위임해야합니다.");
+        }
+        memberClubRepository.deleteMemberClubByClub_IdAndMember_Id(clubId, UUID.fromString(userId));
+    }
+
+    @Transactional
+    public void delegateManager(PrincipalDetails details, UUID memberId, Long clubId) {
+        Club club = clubRepository.findClubById(clubId).orElseThrow(
+                () -> new RuntimeException("존재하지 않는 모임입니다.")
+        );
+        if (!club.getManagerId().toString().equals(details.getUserId())) {
+            throw new RuntimeException("해당 클럽의 모임장이 아님");
+        }
+        if (memberClubRepository.findMemberClubByMember_IdAndClub_Id(memberId, clubId).isEmpty()) {
+            throw new RuntimeException("위임하려는 멤버가 모임 구성원이 아닙니다.");
+        }
+        clubRepository.updateManager(memberId);
+    }
+
+    @Transactional
+    public void expelMember(PrincipalDetails details, UUID memberId, Long clubId) {
+        Club club = clubRepository.findClubById(clubId).orElseThrow(
+                () -> new RuntimeException("존재하지 않는 모임입니다.")
+        );
+        if (!club.getManagerId().toString().equals(details.getUserId())) {
+            throw new RuntimeException("해당 클럽의 모임장이 아님");
+        }
+        if (memberClubRepository.findMemberClubByMember_IdAndClub_Id(memberId, clubId).isEmpty()) {
+            throw new RuntimeException("추방하려는 멤버가 모임 구성원이 아닙니다.");
+        }
+        memberClubRepository.deleteMemberClubByClub_IdAndMember_Id(clubId, memberId);
+    }
+
+    public ClubResponse getClub(Long clubId) {
+        Club club = clubRepository.findClubById(clubId).orElseThrow(
+                () -> new RuntimeException("해당 모임이 존재하지 않습니다.")
+        );
+        return new ClubResponse(club);
+    }
+}
diff --git a/backend/src/main/java/com/project/capstone/common/domain/MemberClub.java b/backend/src/main/java/com/project/capstone/common/domain/MemberClub.java
index b1e7a8035c..2f1c5aaab8 100644
--- a/backend/src/main/java/com/project/capstone/common/domain/MemberClub.java
+++ b/backend/src/main/java/com/project/capstone/common/domain/MemberClub.java
@@ -3,8 +3,12 @@
 import com.project.capstone.club.domain.Club;
 import com.project.capstone.member.domain.Member;
 import jakarta.persistence.*;
+import lombok.AllArgsConstructor;
+import lombok.NoArgsConstructor;
 
 @Entity
+@AllArgsConstructor
+@NoArgsConstructor
 public class MemberClub {
 
     @Id
@@ -12,8 +16,11 @@ public class MemberClub {
     private Long id;
 
     @ManyToOne
+    @JoinColumn(name = "member_id")
     private Member member;
 
     @ManyToOne
+    @JoinColumn(name = "club_id")
     private Club club;
+
 }
diff --git a/backend/src/main/java/com/project/capstone/common/domain/MemberClubRepository.java b/backend/src/main/java/com/project/capstone/common/domain/MemberClubRepository.java
new file mode 100644
index 0000000000..2ed5a20a50
--- /dev/null
+++ b/backend/src/main/java/com/project/capstone/common/domain/MemberClubRepository.java
@@ -0,0 +1,12 @@
+package com.project.capstone.common.domain;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Optional;
+import java.util.UUID;
+
+
+public interface MemberClubRepository extends JpaRepository<MemberClub, Long> {
+    void deleteMemberClubByClub_IdAndMember_Id(Long clubId, UUID memberId);
+    Optional<MemberClub> findMemberClubByMember_IdAndClub_Id(UUID memberId, Long clubId);
+}
diff --git a/backend/src/main/java/com/project/capstone/config/SecurityConfig.java b/backend/src/main/java/com/project/capstone/config/SecurityConfig.java
index dd4baed188..68bbec8aa1 100644
--- a/backend/src/main/java/com/project/capstone/config/SecurityConfig.java
+++ b/backend/src/main/java/com/project/capstone/config/SecurityConfig.java
@@ -22,7 +22,7 @@ public class SecurityConfig {
     @Bean
     SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
         http.authorizeHttpRequests(authorizeRequest ->
-                authorizeRequest.requestMatchers("/auth/**").permitAll()
+                authorizeRequest.requestMatchers("/auth/**", "/club/search/**").permitAll()
                         .anyRequest().authenticated())
                 .csrf(AbstractHttpConfigurer::disable)
                 .cors(AbstractHttpConfigurer::disable)
diff --git a/backend/src/main/java/com/project/capstone/member/domain/Member.java b/backend/src/main/java/com/project/capstone/member/domain/Member.java
index e42c9a1f3b..d518560756 100644
--- a/backend/src/main/java/com/project/capstone/member/domain/Member.java
+++ b/backend/src/main/java/com/project/capstone/member/domain/Member.java
@@ -34,7 +34,6 @@ public class Member {
     private String name;
     private int age;
     private String gender;
-    private String role;
     @CreatedDate
     @Column(name = "created_at")
     private LocalDateTime createdAt;
@@ -52,7 +51,7 @@ public class Member {
     private List<Content> contents = new ArrayList<>();
 
     public Member(SignupRequest request) {
-        this(null, request.email(), request.name(), request.age(), request.gender(), "ROLE_USER", null,
+        this(null, request.email(), request.name(), request.age(), request.gender(), null,
                 new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
     }
 }
diff --git a/backend/src/main/java/com/project/capstone/member/domain/MemberRepository.java b/backend/src/main/java/com/project/capstone/member/domain/MemberRepository.java
index 8351513308..0d6e816388 100644
--- a/backend/src/main/java/com/project/capstone/member/domain/MemberRepository.java
+++ b/backend/src/main/java/com/project/capstone/member/domain/MemberRepository.java
@@ -11,4 +11,5 @@ public interface MemberRepository extends JpaRepository<Member, UUID> {
 
     Member save(Member member);
     Optional<Member> findMemberByEmail(String email);
+    Optional<Member> findMemberById(UUID id);
 }
diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml
index 27d0c13119..96d105d2a2 100644
--- a/backend/src/main/resources/application.yml
+++ b/backend/src/main/resources/application.yml
@@ -8,6 +8,11 @@ spring:
   jpa:
     hibernate:
       ddl-auto: create
+    defer-datasource-initialization: true
+
+  sql:
+    init:
+      mode: always
 
 jwt:
   secret: gDpXHGuTSnwn6IkHoQE0TyrHT4qGDsbAm6L21qSbzUe8s/Nvo2JsiJyawX8fvUD6 Nh4CdIeQxAqnAzysgk+nUw==
\ No newline at end of file
diff --git a/backend/src/main/resources/data.sql b/backend/src/main/resources/data.sql
new file mode 100644
index 0000000000..cd94ec6aaf
--- /dev/null
+++ b/backend/src/main/resources/data.sql
@@ -0,0 +1,43 @@
+insert into member (id, email, name, age, gender, created_at) values
+(UNHEX(REPLACE("3f06af63-a93c-11e4-9797-00505690773f", "-","")), 'example1@gmail.com', '홍길동1', 17, '남자', '2023-05-16 15:48:57.450179');
+insert into member (id, email, name, age, gender, created_at) values
+(UNHEX(REPLACE("8fbcec61-e527-15ee-bd3d-0242ac120002", "-","")), 'example2@gmail.com', '홍길동2', 17, '남자', '2023-05-26 15:48:57.450179');
+insert into member (id, email, name, age, gender, created_at) values
+(UNHEX(REPLACE("8fbcec62-e527-14ee-bd3d-0242ac120002", "-","")), 'example3@gmail.com', '홍길동3', 17, '남자', '2023-05-26 15:48:57.450179');
+insert into member (id, email, name, age, gender, created_at) values
+(UNHEX(REPLACE("8fbcec63-e527-10ee-bd3d-0242ac120002", "-","")), 'example4@gmail.com', '홍길동4', 17, '남자', '2023-05-26 15:48:57.450179');
+insert into member (id, email, name, age, gender, created_at) values
+(UNHEX(REPLACE("8fbcec64-e527-11ee-bd3d-0242ac120002", "-","")), 'example5@gmail.com', '홍길동5', 17, '남자', '2023-05-26 15:48:57.450179');
+insert into member (id, email, name, age, gender, created_at) values
+(UNHEX(REPLACE("8fbcec65-e527-12ee-bd3d-0242ac120002", "-","")), 'example6@gmail.com', '홍길동6', 17, '남자', '2023-05-26 15:48:57.450179');
+
+insert into book (id, title, category_1d, category_2d, category_3d, author, publisher, publish_date) values
+(1, '제목1', '카테고리1', '카테고리2', '카테고리3', '저자1', '출판사1', '2022-04-10');
+insert into book (id, title, category_1d, category_2d, category_3d, author, publisher, publish_date) values
+(2, '제목2', '카테고리1', '카테고리2', '카테고리3', '저자2', '출판사2', '2022-04-10');
+insert into book (id, title, category_1d, category_2d, category_3d, author, publisher, publish_date) values
+(3, '제목3', '카테고리1', '카테고리2', '카테고리3', '저자3', '출판사3', '2022-04-10');
+insert into book (id, title, category_1d, category_2d, category_3d, author, publisher, publish_date) values
+(4, '제목4', '카테고리1', '카테고리2', '카테고리3', '저자4', '출판사4', '2022-04-10');
+insert into book (id, title, category_1d, category_2d, category_3d, author, publisher, publish_date) values
+(5, '제목5', '카테고리1', '카테고리2', '카테고리3', '저자5', '출판사5', '2022-04-10');
+
+
+insert into club (id, manager_id, topic, name, created_at, maximum, public_status, password, book_id) values
+(1, UNHEX(REPLACE("3f06af63-a93c-11e4-9797-00505690773f", "-","")), '역사', '길동1의 모임', '2023-05-26 15:48:57.450179', 10, 'PUBLIC', null, 1);
+insert into club (id, manager_id, topic, name, created_at, maximum, public_status, password, book_id) values
+(2, UNHEX(REPLACE("8fbcec61-e527-15ee-bd3d-0242ac120002", "-","")), '경제', '길동2의 모임', '2023-05-26 15:48:57.450179', 10, 'PUBLIC', null, null);
+insert into club (id, manager_id, topic, name, created_at, maximum, public_status, password, book_id) values
+(3, UNHEX(REPLACE("8fbcec62-e527-14ee-bd3d-0242ac120002", "-","")), '종교', '길동3의 모임', '2023-05-26 15:48:57.450179', 10, 'PUBLIC', null, 2);
+insert into club (id, manager_id, topic, name, created_at, maximum, public_status, password, book_id) values
+(4, UNHEX(REPLACE("8fbcec63-e527-10ee-bd3d-0242ac120002", "-","")), '사회', '길동4의 모임', '2023-05-26 15:48:57.450179', 10, 'PUBLIC', null, 3);
+
+insert into club (id, manager_id, topic, name, created_at, maximum, public_status, password, book_id) values
+(5, UNHEX(REPLACE("3f06af63-a93c-11e4-9797-00505690773f", "-","")), '역사', '길동1의 모임', '2023-05-26 15:48:57.450179', 10, 'PUBLIC', null, 1);
+insert into club (id, manager_id, topic, name, created_at, maximum, public_status, password, book_id) values
+(6, UNHEX(REPLACE("8fbcec61-e527-15ee-bd3d-0242ac120002", "-","")), '경제', '길동2의 모임', '2023-05-26 15:48:57.450179', 10, 'PUBLIC', null, null);
+insert into club (id, manager_id, topic, name, created_at, maximum, public_status, password, book_id) values
+(7, UNHEX(REPLACE("8fbcec62-e527-14ee-bd3d-0242ac120002", "-","")), '종교', '길동3의 모임', '2023-05-26 15:48:57.450179', 10, 'PUBLIC', null, 2);
+insert into club (id, manager_id, topic, name, created_at, maximum, public_status, password, book_id) values
+(8, UNHEX(REPLACE("8fbcec63-e527-10ee-bd3d-0242ac120002", "-","")), '사회', '길동4의 모임', '2023-05-26 15:48:57.450179', 10, 'PUBLIC', null, 3);
+