RFP 3๋ฒ ํ๋ก์ ํธ
Deploy ๋ฐฐํฌ ํ์ผ
DockerFiles ๋์ปค ํ์ผ๋ค
| EC2 Scripts
/home/ubuntu/spring/docker_script.sh
docker login ghcr.io -u Domae-back-end -p {token}
docker pull ghcr.io/boiled-potatoes-kdt/be/spring:latest
BLUE_SPRING="spring-blue"
GREEN_SPRING="spring-green"
if [ "$(docker ps -q -f name=$BLUE_SPRING)" ]; then
ACTIVE_SPRING=$BLUE_SPRING
IDELE_SPRING=$GREEN_SPRING
OLD_PORT="8081"
PORT=8080
else
ACTIVE_SPRING=$GREEN_SPRING
IDELE_SPRING=$BLUE_SPRING
OLD_PORT="8080"
PORT=8081
fi
docker run -d \
--name $IDELE_SPRING \
-p $PORT:8080 \
"ghcr.io/boiled-potatoes-kdt/be/spring"
sleep 30
sudo sed -i "s/http:\/\/localhost:$OLD_PORT/http:\/\/localhost:$PORT/g" "/etc/nginx/sites-available/default"
sudo nginx -t
sudo systemctl restart nginx
docker stop $ACTIVE_SPRING
docker rm $ACTIVE_SPRING
/home/ubuntu/db/docker_script.sh
docker login ghcr.io -u Domae-back-end -p {TOKEN}
docker pull ghcr.io/boiled-potatoes-kdt/be/mysql:latest
CONTAINER_NAME="mysql"
IMAGE_NAME="ghcr.io/boiled-potatoes-kdt/be/mysql:latest"
if [ "$(docker ps -q -f name=$CONTAINER_NAME)" ]; then
echo "์ปจํ
์ด๋๊ฐ ์คํ์ค์
๋๋ค."
else
echo "์ปจํ
์ด๋๊ฐ ์คํ์ค์ด์ง ์์ต๋๋ค. ์๋ก ์์ํฉ๋๋ค๋์ฅ"
docker rm mysql
docker run -d --name $CONTAINER_NAME -p 3306:3306 -e MYSQL_ROOT_PASSWORD={PASSWORD} $IMAGE_NAME
fi
/home/ubuntu/redis/docker_script.sh
docker login ghcr.io -u Domae-back-end -p {TOKEN}
docker pull ghcr.io/boiled-potatoes-kdt/be/redis:latest
CONTAINER_NAME="redis"
IMAGE_NAME="ghcr.io/boiled-potatoes-kdt/be/redis:latest"
if [ "$(docker ps -q -f name=$CONTAINER_NAME)" ]; then
echo "์ปจํ
์ด๋๊ฐ ์คํ์ค์
๋๋ค."
else
echo "์ปจํ
์ด๋๊ฐ ์คํ์ค์ด์ง ์์ต๋๋ค. ์๋ก ์์ํฉ๋๋ค๋์ฅ"
docker rm redis
docker run -d --name $CONTAINER_NAME -p 6379:6379 -e REDIS_PASSWORD={PASSWORD} $IMAGE_NAME
fi
- Spring Boot, Security, JPA
- MySQL
- Redis
- AWS, Docker
- Github, GithubAction
- Discord, Slack, Notion
- main (DDD ๊ธฐ๋ฒ)
- domain
- test1(๋๋ฉ์ธ๋ช
)
- controller
- service
- model
- request
- response
- type
- entity
- repository
- exception
- test2(๋๋ฉ์ธ๋ช )
- test1(๋๋ฉ์ธ๋ช
)
- global
- config
- exception
- model
- util
- api
- validation
- domain
- ์นด๋ฉ ํ๊ธฐ๋ฒ
- Entity ์์ฑ์์ โ Entity X โ ๋ช ์ฌ
- DTO class -> Record class
- request, response โ **Request.java, **Response.java
- Controller, Service ๋งค์๋๋ช
- Controller
- ์์: getUserById, createUser, updateUser, deleteUser ๋ฑ
- Service
- ์์: processOrder, calculateTotalPrice, cancelReservation ๋ฑ
- Controller
- main (์ต์ข ๋ณธ)
- release (๋ฐฐํฌ)
- develop (ํตํฉ)
- feature/... (๊ธฐ๋ฅ๋ค)
- refactor/... (์์ ๋ธ๋์น)
- ์์ โ feature/trip - feature/trip/discord
- feat:์๋ก์ด ๊ธฐ๋ฅ ์ถ๊ฐ
- fix:๋ฒ๊ทธ ์์
- docs:๋ฌธ์ ์์
- style:์ฝ๋ formatting, ์ธ๋ฏธ์ฝ๋ก (;)๋๋ฝ, ์ฝ๋ ๋ณ๊ฒฝ์ด ์๋ ๊ฒฝ์ฐ
- refactor:์ฝ๋ ๋ฆฌํฉํฐ๋ง
- test:ํ ์คํธ ์ฝ๋, ๋ฆฌํฉํฐ๋ง ํ ์คํธ ์ฝ๋ ์ถ๊ฐ(ํ๋ก๋์ ์ฝ๋ ๋ณ๊ฒฝ X)
- chore:๋น๋ ์ ๋ฌด ์์ , ํจํค์ง ๋งค๋์ ์์ (ํ๋ก๋์ ์ฝ๋ ๋ณ๊ฒฝ X)
- comment:ํ์ํ ์ฃผ์ ์ถ๊ฐ ๋ฐ ๋ณ๊ฒฝ
- rename:ํ์ผ ํน์ ํด๋๋ช ์ ์์ ํ๊ฑฐ๋ ์ฎ๊ธฐ๋ ์์ ๋ง์ธ ๊ฒฝ์ฐ
- remove:ํ์ผ์ ์ญ์ ํ๋ ์์ ๋ง ์ํํ ๊ฒฝ์ฐ
- !BREAKING CHANGE:์ปค๋ค๋ API ๋ณ๊ฒฝ์ ๊ฒฝ์ฐ
- !HOTFIX:๊ธํ๊ฒ ์น๋ช ์ ์ธ ๋ฒ๊ทธ๋ฅผ ๊ณ ์ณ์ผ ํ๋ ๊ฒฝ์ฐ
- /api ๊ณตํต์ ์ผ๋ก ๋ค์ด๊ฐ๊ณ ๋ค์์ผ๋ก๋ ๊ถํ ๊ทธ ๋ค์์ผ๋ก๋ API ๋ช ์ธ์์ ๋ฐ๋ฅธ URL
- /api/customer/..
- /api/influence/..
- Entity โโ DTO
public record TestRequest(
String name,
String title
) {
public static TestRequest from(TestEntity entity) {
return new TestRequest(
entity.getName(),
entity.getTitle()
);
}
}@Entity
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class TestEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String name;
public static TestEntity from(TestRequest request) {
return TestEntity.builder()
.title(request.title())
.name(request.name())
.build();
}
}- Exception handling
public interface ErrorCode {
HttpStatus getStatus();
String getMsg();
}@RequiredArgsConstructor
public abstract class GlobalException extends RuntimeException {
@Getter
private final ErrorCode errorCode;
public abstract void exceptionHandling();
public GlobalResponse getErrorResponse() {
return new GlobalResponse(
errorCode.getMsg(),
errorCode.getStatus()
);
}
}public record GlobalResponse(
String msg,
HttpStatus status
) {
}@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(GlobalException.class)
public ResponseEntity errorResponse(GlobalException ex) {
ex.exceptionHandling();
return API.ERROR(ex);
}
}- API ์๋ต
@Data
@AllArgsConstructor
@NoArgsConstructor
public class API {
public static ResponseEntity OK(Object data) {
return ResponseEntity.ok(data);
}
public static ResponseEntity OK() {
return ResponseEntity.ok(new GlobalResponse("์ฑ๊ณต", HttpStatus.OK));
}
public static ResponseEntity ERROR(GlobalException ex) {
return ResponseEntity.status(ex.getErrorCode().getStatus())
.body(ex.getErrorResponse());
}
}- validation - ๊ธฐ์กด ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ํ๋ validation ์กด์ฌํ์ง ์์ ๊ฒฝ์ฐ ์ปค์คํ
์ด๋
ธํ
์ด์
๋์ฒด
-
์ปค์คํ ์ด๋ ธํ ์ด์
@Target({ElementType.PARAMETER, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = TestValidator.class) public @interface TestValidation { String message() default "Invalid category"; Class[] groups() default {}; Class[] payload() default {}; }
public class TestValidator implements ConstraintValidator<TestValidation, String> { @Override public void initialize(TestValidation constraintAnnotation) { ConstraintValidator.super.initialize(constraintAnnotation); } @Override public boolean isValid(String arg, ConstraintValidatorContext constraintValidatorContext) { return true; } }
-
๊ธฐ์กด ๋ผ์ด๋ธ๋ฌ๋ฆฌ
@RestController public class TestController { @PostMapping public ResponseEntity test( @RequestBody @Valid TestRequest request ) { return API.OK(); } }
public record TestRequest( @Length(max = 1, message = "๋ด๊ฐ ๋ง๋ ์๋ฌ") String name ) { }
-
