Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
2ac47af
[chore]: gradle 파일 각종 버전 인프런 김영한님 강의와 동일하게
ji-hyeon97 Jan 16, 2023
dc5e594
[feat] : welcome page 만들기
ji-hyeon97 Jan 16, 2023
6c3630a
[feat] : 정적컨텐츠, MVC와 템플릿 엔진, API 기능 확인
ji-hyeon97 Jan 16, 2023
eccec7b
[feat] : 회원 도메인과 회원 리포지토리(InMemory database)구현
ji-hyeon97 Jan 16, 2023
5ad163d
[feat] : 회원 레포지토리 테스트코드 구현
ji-hyeon97 Jan 16, 2023
c0e5106
[feat] : 회원 서비스 구현
ji-hyeon97 Jan 17, 2023
1a6bd44
[feat] : 회원 서비스 테스트코드 구현, 의존성 주입고려
ji-hyeon97 Jan 17, 2023
1d9eb36
[feat] : 컴포넌트 스캔과 자동 의존관계 설정
ji-hyeon97 Jan 17, 2023
9917d98
[feat] : 자바 코드로 직접 스프링 빈 등록하기
ji-hyeon97 Jan 17, 2023
79e80dc
[feat] : 회원 웹기능(홈화면, 등록, 조회)
ji-hyeon97 Jan 17, 2023
d39d643
[chore] : h2, jdbc 관련 라이브러리 추가
ji-hyeon97 Jan 17, 2023
dd3825d
[feat] : jdbc 레포지토리 구현
ji-hyeon97 Jan 17, 2023
ffc1a40
[test] : 회원 서비스 스프링 통합 테스트 구현
ji-hyeon97 Jan 17, 2023
b45495f
[feat] : 스프링 JdbcTemplate 회원 리포지토리 구현
ji-hyeon97 Jan 17, 2023
0babe65
[chore] : jpa 라이브러리 추가 및 설정
ji-hyeon97 Jan 17, 2023
562848a
[feat] : jpa 레포지토리, 엔티티매핑, 트랜잭션 설정
ji-hyeon97 Jan 17, 2023
5339920
[feat] : spring data jpa
ji-hyeon97 Jan 17, 2023
c7887ef
[feat] : 요청된 메서드 수행시간 뽑기(공통관심사항) aop
ji-hyeon97 Jan 17, 2023
220fc1d
[refactor] : 필요없는 어노테이션 제거
ji-hyeon97 Jan 17, 2023
f0028c9
[docs] : README.md 파일 작성
ji-hyeon97 Jan 18, 2023
a39dd72
[remove]: 2주차 과제를 위해 파일 삭제
ji-hyeon97 Jan 23, 2023
faf82a9
[remove]: 2주차 과제를 위해 파일 삭제(타임리프 파일들)
ji-hyeon97 Jan 23, 2023
d80bcb7
[chore]: 스프링 시큐리티, JWT 세팅
ji-hyeon97 Jan 23, 2023
28c472e
[feat]: swagger api 문서 자동화
ji-hyeon97 Jan 23, 2023
ed5167a
[feat]: 응답 DTO 형식 통일시키기
ji-hyeon97 Jan 24, 2023
1e2618d
[docs]:README.md 파일 수정
ji-hyeon97 Jan 26, 2023
bff7014
[feat]: User 엔티티 생성
ji-hyeon97 Jan 26, 2023
44bacdf
[feat]: yml 파일 업로드 X
ji-hyeon97 Jan 26, 2023
38b107a
[chore]: 롬복 설정 및 스프링 부트 버전 올리기
ji-hyeon97 Jan 27, 2023
fde437c
[feat]: SecurityFilterChain 수동으로 스프링 컨테이너 등록
ji-hyeon97 Jan 27, 2023
1441cfc
[feat]: User 엔티티 닉네임 추가 & UserRepository
ji-hyeon97 Jan 27, 2023
2b726da
[feat]: 회원가입, 로그인 요청 DTO
ji-hyeon97 Jan 27, 2023
01bc87b
[feat]: JWT payload 에 들어갈 subject 객체 생성
ji-hyeon97 Jan 27, 2023
a4704e3
[feat]: JWT 토큰 생성 및 JWT 인증 필터 구현
ji-hyeon97 Jan 27, 2023
2d556a0
[feat]: 회원가입 & 로그인 API 구현
ji-hyeon97 Jan 27, 2023
ae966eb
[fix]: JWT 토큰 claim 에 권한 추가
ji-hyeon97 Jan 27, 2023
6a01a36
[feat]: 필터단계에서의 JWT 예외처리 구현
ji-hyeon97 Jan 27, 2023
1f81df5
[chore]: SMPT 이메일 인증 의존성 주입
ji-hyeon97 Jan 27, 2023
3fd9d89
[feat]: SMPT 이메일 인증 API 구현
ji-hyeon97 Jan 27, 2023
57c3143
[rename]: 파일위치 이동
ji-hyeon97 Jan 27, 2023
e36257d
[add]: 회원가입 기본프로필 이미지 적용
ji-hyeon97 Jan 27, 2023
f5be1b0
[chore]: redis 메모리 의존성 주입
ji-hyeon97 Jan 27, 2023
3249886
[chore]: redis 설정정보 스프링 빈 수동설정 & 데이터 CRUD
ji-hyeon97 Jan 28, 2023
6851e06
[feat]: access 토큰 재발급 API
ji-hyeon97 Jan 28, 2023
e78f132
[docs]: README.md 파일 update
ji-hyeon97 Jan 28, 2023
c063109
[refactor]: access 토큰 재발급 API 수정
ji-hyeon97 Jan 29, 2023
3089c8c
[chore]: AWS S3 연동
ji-hyeon97 Jan 30, 2023
f733619
[feat]: 개인정보 수정 API 구현(닉네임, 회원프로필)
ji-hyeon97 Jan 30, 2023
ec9f464
[docs]: README.md 파일 update
ji-hyeon97 Jan 30, 2023
3cee14c
[feat]: 초기 ERD 구현
ji-hyeon97 Jan 30, 2023
949b49c
[rename]: 클래스 이름 변경
ji-hyeon97 Jan 30, 2023
e61b3ad
[feat]: 모든 엔티티에 JPA Auditing 기능 구현(엔티티 값 생성시간, 수정시간)
ji-hyeon97 Jan 30, 2023
1b8af84
[feat]: 중고거래 글쓰기(다중 이미지 업로드 구현)
ji-hyeon97 Feb 1, 2023
bf47025
[chore]: Hibernate5Module 의존성 주입
ji-hyeon97 Feb 1, 2023
34c09c1
[fix]: 지연로딩시 프록시객체 json 생성시 에러 해결
ji-hyeon97 Feb 1, 2023
9348400
[feat]: 게시판 보기 API 구현
ji-hyeon97 Feb 1, 2023
ea3b15e
[refactor]: 게시판과 상품은 일대일 관계로 수정
ji-hyeon97 Feb 1, 2023
aabefcd
[refactor]: board엔티티를 items엔티티로 변경(도메인, 엔티티는 화면에 종속되지 않는다)
ji-hyeon97 Feb 2, 2023
9c47ba1
[refactor]: user관련 dto는 user 패키지에서 관리
ji-hyeon97 Feb 2, 2023
930beb3
[feat]: JPA 양방향 관계 엔티티 동시 저장 구현(여러개의 상품이미지, 상품 정보)
ji-hyeon97 Feb 2, 2023
2ee4871
[feat]: enum 객체 값 응답(상품 카테고리 API)
ji-hyeon97 Feb 2, 2023
a2b1cd6
[feat]: 좋아요 API 구현(좋아요 완료 & 좋아요 취소 구현)
ji-hyeon97 Feb 3, 2023
a2fb7d0
[feat]: 상품 삭제하기 구현
ji-hyeon97 Feb 3, 2023
ea16289
[refactor]: post 요청시 응답값은 해당 엔티티의 아이디 값으로 변경
ji-hyeon97 Feb 3, 2023
bb8bff5
[feat]: 게시글 삭제 시 어려개의 이미지 파일은 DB 뿐만 아니라 S3버킷에도 삭제 API 구현
ji-hyeon97 Feb 3, 2023
2925870
[feat]: 상품 정보 수정 API 구현
ji-hyeon97 Feb 3, 2023
a52beae
[docs]: README.md 파일 업데이트
ji-hyeon97 Feb 3, 2023
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ out/

### VS Code ###
.vscode/

*.yml
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
> 당근마켓 backend server project

<p align="center">
<img width="150" src="https://user-images.githubusercontent.com/79920930/214882442-15270259-4aec-47af-ae91-0f5a0d92086b.png">
<img width="150" src="https://user-images.githubusercontent.com/79920930/214882451-7175d9d4-40ae-4b82-8ca1-911c41c763d8.png">
<img width="150" src="https://user-images.githubusercontent.com/79920930/214884316-9b45406e-3861-4818-8a88-824d05b618fa.png">
<img width="150" src="https://user-images.githubusercontent.com/79920930/214884325-862906ff-7d98-47a3-88ea-00d3232a995c.png">
<img width="150" src="https://user-images.githubusercontent.com/79920930/215382262-0effafe5-3bd4-4197-8bd5-8b22bcdf9500.png">
<img width="150" src="https://user-images.githubusercontent.com/79920930/215382389-f8c5e690-9044-4280-91f3-023d1ddeca17.png">
<img width="150" src="https://user-images.githubusercontent.com/79920930/215382392-35254e88-9f2a-4ea6-8af3-2e95071bb662.png">
<img width="150" src="https://user-images.githubusercontent.com/79920930/215382394-902c99a9-0c1a-4613-aeae-9d7fe454d005.png">
<img width="150" height = "327" src="https://user-images.githubusercontent.com/79920930/215384487-2136bc18-23c0-4376-9473-5d644a9be0ca.png">
<img width="150" src="https://user-images.githubusercontent.com/79920930/215384314-0e4b743e-b57c-4228-be2e-0accc70707e7.png">
<img width="152" src="https://user-images.githubusercontent.com/79920930/215383493-21f81273-d9eb-4b4b-99ea-98a34cebe003.png">
<img width="151" src="https://user-images.githubusercontent.com/79920930/215383494-41c04fbf-1d43-47f7-8ccf-c6f901b88a0d.png">



</p>

<hr>

### 1주차

> 회원가입 및 로그인 기능
- 요구사항 분석
- 도메인 모델 및 테이블 설계 고민
- JWT 구현 및 테스트 코드 작성
- 이메일 인증하기

<br>

### 2주차

> 초기 도메인 모델 및 테이블 설계 구현
- 상품 등록, 수정, 삭제 기능
- 마이페이지(회원 수정 기능)

<br>

<hr>

### 3주차

> 여러개의 테이블이 얽힌 화면에 맞는 DTO설정 및 API구현

<hr>

## 개발 기록

- 회원가입 & 로그인 API 구현 ```commit``` : [2d556a0](https://github.com/ji-hyeon97/Spring-JPA-study/commit/2d556a0b00a58f70c7d10b7e15a1170d2d8b27a8)
- SMPT 이메일 인증 API 구현 ```commit``` : [3fd9d89](https://github.com/ji-hyeon97/Spring-JPA-study/commit/3fd9d898213aa011d04684e3c5fa40c10f0d1ca9)
- access 토큰 재발급 API 구현 ```commit``` : [6851e06](https://github.com/ji-hyeon97/Spring-JPA-study/commit/6851e06e80ce4e7688d169878cca5806d32592fd)
- AWS S3 연동 및 다중 이미지 업로드 API 구현 ```commit``` : [930beb3](https://github.com/ji-hyeon97/Spring-JPA-study/commit/930beb3b92a0449e94f22ce436d5174a7d4c6d46)
- 좋아요 API 구현 ```commit``` : [a2b1cd6](https://github.com/ji-hyeon97/Spring-JPA-study/commit/a2b1cd6f38c901c66067dff0e09e7fc55ac2c572)
- 상품 수정 및 삭제 API 구현 ```commit``` : [2925870](https://github.com/ji-hyeon97/Spring-JPA-study/commit/29258702188606488dc7aabc1c665a78b34096a4) , [a2fb7d0](https://github.com/ji-hyeon97/Spring-JPA-study/commit/a2fb7d08234ea39b7c3d08f1a4b474ed20188984)
28 changes: 18 additions & 10 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
plugins {
id 'org.springframework.boot' version '2.7.4'
id 'io.spring.dependency-management' version '1.0.14.RELEASE'
id 'java'
id 'org.springframework.boot' version '3.0.1'
id 'io.spring.dependency-management' version '1.1.0'
}

group = 'com.dku'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
sourceCompatibility = '11'

configurations {
compileOnly {
Expand All @@ -19,16 +18,25 @@ repositories {
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation group: 'io.jsonwebtoken', name: 'jjwt', version: '0.9.1'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation group: 'io.springfox', name: 'springfox-swagger2', version: '2.9.2'
implementation group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.9.2'
implementation 'org.springframework.boot:spring-boot-starter-mail:2.7.1'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.12.349'
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
runtimeOnly 'com.h2database:h2'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}

tasks.named('test') {
test {
useJUnitPlatform()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@EnableJpaAuditing
@SpringBootApplication
public class SpringStudyApplication {

Expand Down
30 changes: 30 additions & 0 deletions src/main/java/com/dku/springstudy/config/AwsS3Config.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.dku.springstudy.config;

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AwsS3Config {
@Value("${spring.cloud.aws.credentials.accessKey}")
private String accessKey;

@Value("${spring.cloud.aws.credentials.secretKey}")
private String secretKey;

@Value("${spring.cloud.aws.region.static}")
private String region;

@Bean
public AmazonS3Client amazonS3Client() {
BasicAWSCredentials awsCreds = new BasicAWSCredentials(accessKey, secretKey);
return (AmazonS3Client) AmazonS3ClientBuilder.standard()
.withRegion(region)
.withCredentials(new AWSStaticCredentialsProvider(awsCreds))
.build();
}
}
43 changes: 43 additions & 0 deletions src/main/java/com/dku/springstudy/config/MailConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.dku.springstudy.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;

import java.util.Properties;

@Configuration
public class MailConfig{
@Value("${mail.host}")
private String host;

@Value("${mail.id}")
private String id;

@Value("${mail.password}")
private String password;

@Bean
public JavaMailSender javaMailService() {
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
javaMailSender.setHost(host);
javaMailSender.setUsername(id);
javaMailSender.setPassword(password);
javaMailSender.setPort(465);
javaMailSender.setJavaMailProperties(getMailProperties());
return javaMailSender;
}

private Properties getMailProperties() {
Properties properties = new Properties();
properties.setProperty("mail.transport.protocol", "smtp");
properties.setProperty("mail.smtp.auth", "true");
properties.setProperty("mail.smtp.starttls.enable", "true");
properties.setProperty("mail.debug", "true");
properties.setProperty("mail.smtp.ssl.trust","smtp.naver.com");
properties.setProperty("mail.smtp.ssl.enable","true");
return properties;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.dku.springstudy.config.security;

import com.dku.springstudy.config.security.jwt.CustomAuthenticationEntryPoint;
import com.dku.springstudy.config.security.jwt.JwtAuthenticationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.filter.CorsFilter;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class AppConfig {

private final JwtAuthenticationFilter jwtAuthenticationFilter;
private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

@Bean
public PasswordEncoder passwordEncoder(){
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
return http
.csrf().disable()
.httpBasic().disable()
.exceptionHandling()
.authenticationEntryPoint(customAuthenticationEntryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/i/**").access("hasRole('ADMIN')")
.antMatchers("/account/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterAfter(jwtAuthenticationFilter, CorsFilter.class)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.dku.springstudy.config.security;

import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class LazyLoadingProxyConfig {
@Bean
Hibernate5Module hibernate5Module() {
return new Hibernate5Module();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.dku.springstudy.config.security;


import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
@EnableRedisRepositories
public class RedisConfig {

@Value("${spring.redis.host}")
private String host;

@Value("${spring.redis.port}")
private int port;

@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(host, port);
}

@Bean
public RedisTemplate<String, String> redisTemplate() {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory());
return redisTemplate;
}
}
31 changes: 31 additions & 0 deletions src/main/java/com/dku/springstudy/config/security/RedisDao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.dku.springstudy.config.security;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.time.Duration;

@Component
public class RedisDao {

private final RedisTemplate<String, String> redisTemplate;

public RedisDao(RedisTemplate<String, String> redisTemplate) {
this.redisTemplate = redisTemplate;
}

public void setValues(String key, String data, Duration duration) {
ValueOperations<String, String> values = redisTemplate.opsForValue();
values.set(key, data, duration);
}

public String getValues(String key) {
ValueOperations<String, String> values = redisTemplate.opsForValue();
return values.get(key);
}

public void deleteValues(String key) {
redisTemplate.delete(key);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.dku.springstudy.config.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfig {
private ApiInfo commonInfo(){
return new ApiInfoBuilder()
.title("CarrotMarket API")
.version("1.0")
.build();
}
@Bean
public Docket allApi(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("USER")
.useDefaultResponseMessages(false)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.apiInfo(commonInfo());
}
}
Loading