Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

충남대 BE_송민주 2주차 과제 (2단계) #281

Open
wants to merge 23 commits into
base: mingjuu
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
9c7bf8e
0단계 README 작성
mingjuu Jul 4, 2024
42c669a
docs : 피드백 반영 및 코드 옮기기 완료
mingjuu Jul 4, 2024
951c1c4
docs : 1단계 README.md 작성
mingjuu Jul 4, 2024
f3dd996
docs : 의존성 추가
mingjuu Jul 4, 2024
56cde11
feat : 유효성 검사 어노테이션 추가 (조건값)
mingjuu Jul 4, 2024
292d6d5
docs : 어노테이션 주석 추가
mingjuu Jul 4, 2024
0939a4b
유효성 검즈을 위한 @Valid 어노테이션 사용 및 BindingResult 객체를 통한 검증 결과 처리. 만약 실패할 경우…
mingjuu Jul 4, 2024
713bc21
유효성 검사에서 에러 발생시, 에러메세지 표시 (BindingResult 사용)
mingjuu Jul 4, 2024
a47c76c
docs : 구현한 기능 체크
mingjuu Jul 4, 2024
25f50dd
fix : 정규표현식 수정 ( 한국어로/영어로 입력 가능하도록 )
mingjuu Jul 4, 2024
797d894
docs : 의존성 추가
mingjuu Jul 4, 2024
9b49af6
docs : README.md 파일 작성
mingjuu Jul 4, 2024
eede540
docs : applicaation.properties 빠진내용 붙이기
mingjuu Jul 4, 2024
77c3aca
feat : 회원 도메인 작성
mingjuu Jul 5, 2024
d439e12
회원 정보를 저장한 DB 생성
mingjuu Jul 5, 2024
7d9c734
faet : 회원 정보를 저장하고 이메일을 통해 회원을 찾는 레포 레파지토리 생성
mingjuu Jul 5, 2024
f92b2a4
feat : 로그인 및 회원가입, 토큰 생성 기능 추가
mingjuu Jul 5, 2024
2b6f140
feat : 로그인 및 회원가입 컨트롤러 작성
mingjuu Jul 5, 2024
6d86c5a
docs : 의존성 추가 및 프로펄티스 수정
mingjuu Jul 5, 2024
46c8f67
docs : 카카오 문구를 사용하였는지, 검증하는 로직 추가
mingjuu Jul 5, 2024
7b11bdf
docs : README.md 기능 구현 체크
mingjuu Jul 5, 2024
521ced6
docs : 추가 구현이 필요했던 카카오 문자 사용여부 구현 완료 작성
mingjuu Jul 5, 2024
89f9414
Merge branch 'mingjuu' into step2
mingjuu Jul 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@

## 1단계 요구사항 - 유효성 검사 및 예외 처리
- 상품을 추가하거나 수정할 때
- [] 공백을 포함하여 최대 15자까지 입력하였는지 유효성을 검증한다.
- [] 가능한 특수문자(( ), [ ], +, -, &, /, _)를 사용하였는지 검증한다.
- [x] 공백을 포함하여 최대 15자까지 입력하였는지 유효성을 검증한다.
- [x] 가능한 특수문자(( ), [ ], +, -, &, /, _)를 사용하였는지 검증한다.
- [x] "카카오"가 포함된 문구는 담당 MD와 협의한 경우에만 사용할 수 있다. (이를 클라이언트에게 적절히 알리기


## 2단계 요구사항 - 회원 가입
- [x] 회원은 이메일과 비밀번호를 입력하여 가입한다.
- [x] 토큰을 받으려면 이메일과 비밀번호를 보내야 하며, 가입한 이메일과 비밀번호가 일치하면 토큰이 발급된다.
- [x] 토큰을 생성하는 방법에는 여러 가지가 있다. 방법 중 하나를 선택한다.
13 changes: 8 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,17 @@ repositories {
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'jakarta.persistence:jakarta.persistence-api:3.1.0'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
runtimeOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation 'org.springframework.boot:spring-boot-starter-validation'

}

tasks.named('test') {
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/gift/config/DatabaseInitializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,13 @@ public void initialize() {
System.out.println("Executing SQL: " + sql);
jdbcTemplate.execute(sql);
System.out.println("Table kakaoProduct created successfully");

String sql2 = "CREATE TABLE IF NOT EXISTS members (" +
"id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
"email VARCHAR(255) NOT NULL, " +
"password VARCHAR(255) NOT NULL)";
System.out.println("Executing SQL: " + sql2);
jdbcTemplate.execute(sql2);
System.out.println("Table members created successfully");
}
}
31 changes: 31 additions & 0 deletions src/main/java/gift/controller/MemberController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package gift.controller;

import gift.domain.Member;
import gift.service.MemberService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/members")
public class MemberController {
private final MemberService memberService;

public MemberController(MemberService memberService) {
this.memberService = memberService;
}

@PostMapping("/register")
public ResponseEntity<String> register(@RequestBody Member member) {
memberService.register(member);
return ResponseEntity.ok("User registered successfully");
}

@PostMapping("/login")
public ResponseEntity<String> login(@RequestBody Member member) {
String token = memberService.login(member.getEmail(), member.getPassword());
return ResponseEntity.ok(token);
}
}
14 changes: 12 additions & 2 deletions src/main/java/gift/controller/ProductController.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,15 @@ public String newProductForm(Model model) {
}

@PostMapping
public String createProduct(@Valid @ModelAttribute Product product, BindingResult bindingResult) {
public String createProduct(@Valid @ModelAttribute Product product, BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
return "productForm";
}
// "카카오" 문구 포함 여부 확인하는 로직 추가
if (product.getName().contains("카카오")) {
model.addAttribute("errorMessage", "담당 MD와 협의된 경우에만 '카카오'를 포함할 수 있습니다.");
return "productForm";
}
productRepository.save(product);
return "redirect:/api/products";
}
Expand All @@ -46,10 +51,15 @@ public String editProductForm(@PathVariable Long id, Model model) {
}

@PostMapping("/edit/{id}")
public String updateProduct(@PathVariable Long id, @Valid @ModelAttribute Product updatedProduct, BindingResult bindingResult) {
public String updateProduct(@PathVariable Long id, @Valid @ModelAttribute Product updatedProduct, BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
return "productForm";
}
// "카카오" 문구 포함 여부 확인
if (updatedProduct.getName().contains("카카오")) {
model.addAttribute("errorMessage", "담당 MD와 협의된 경우에만 '카카오'를 포함할 수 있습니다.");
return "productForm";
}
updatedProduct.setId(id);
productRepository.update(updatedProduct);
return "redirect:/api/products";
Expand Down
39 changes: 39 additions & 0 deletions src/main/java/gift/domain/Member.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package gift.domain;

public class Member {
private Long id;
private String email;
private String password;

public Member() {}

public Member(String email, String password) {
this.email = email;
this.password = password;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}
}

2 changes: 1 addition & 1 deletion src/main/java/gift/domain/Product.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class Product {

@NotBlank(message = "Name cannot be blank")
@Size(max = 15, message = "Name cannot be longer than 15 characters")
@Pattern(regexp = "^[\\w\\s\\(\\)\\[\\]+\\-&/_]*$", message = "Name contains invalid characters") // 정규표현식을 통한 특수 문자 제어
@Pattern(regexp = "^[ㄱ-ㅎ가-힣a-zA-Z0-9\\s\\(\\)\\[\\]+\\-&/_]*$", message = "이름에 유효하지 않은 문자가 포함되어 있습니다")
private String name;
private int price;
private String imageUrl;
Expand Down
48 changes: 48 additions & 0 deletions src/main/java/gift/repository/MemberRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package gift.repository;

import gift.domain.Member;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Optional;

@Repository
public class MemberRepository {
private final JdbcTemplate jdbcTemplate;

public MemberRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

public void save(Member member) {
String sql = "INSERT INTO members (email, password) VALUES (?, ?)";
jdbcTemplate.update(sql, member.getEmail(), member.getPassword());
}

public Optional<Member> findByEmail(String email) {
String sql = "SELECT * FROM members WHERE email = ?";
try {
Member member = jdbcTemplate.queryForObject(sql, new Object[]{email}, new MemberRowMapper());
return Optional.ofNullable(member);
} catch (Exception e) {
return Optional.empty();
}
}

private static class MemberRowMapper implements RowMapper<Member> {
@Override
public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setEmail(rs.getString("email"));
member.setPassword(rs.getString("password"));
return member;
}
}
}



45 changes: 45 additions & 0 deletions src/main/java/gift/service/MemberService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package gift.service;

import gift.domain.Member;
import gift.repository.MemberRepository;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.Optional;

@Service
public class MemberService {
private final MemberRepository memberRepository;
@Value("${jwt.secret}")
private String secretKey;

public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}

public void register(Member member) {
memberRepository.save(member);
}

public String login(String email, String password) {
Optional<Member> member = memberRepository.findByEmail(email);
if (member.isPresent() && member.get().getPassword().equals(password)) {
return generateToken(member.get());
} else {
throw new RuntimeException("Invalid email or password");
}
}

private String generateToken(Member member) {
return Jwts.builder()
.setSubject(member.getId().toString())
.claim("email", member.getEmail())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1 hour
.signWith(SignatureAlgorithm.HS256, secretKey.getBytes())
.compact();
}
}
6 changes: 6 additions & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
spring.application.name=spring-gift
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.h2.console.enabled=true
jwt.secret=Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E=
2 changes: 2 additions & 0 deletions src/main/resources/templates/productForm.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ <h1>Product Form</h1>
<input type="url" class="form-control" id="imageUrl" th:field="*{imageUrl}" required>
<div th:if="${#fields.hasErrors('imageUrl')}" class="alert alert-danger" th:errors="*{imageUrl}">Image URL Error</div>
</div>
<!-- 에러 메시지 표시 -->
<div th:if="${errorMessage != null}" class="alert alert-danger" th:text="${errorMessage}">Error Message</div>
<button type="submit" class="btn btn-primary">Submit</button>
<a href="/api/products" class="btn btn-secondary">Cancel</a>
</form>
Expand Down