https://school.programmers.co.kr/learn/courses/30/lessons/42840
— 문제 설명
수포자는 수학을 포기한 사람의 준말입니다. 수포자 삼인방은 모의고사에 수학 문제를 전부 찍으려 합니다. 수포자는 1번 문제부터 마지막 문제까지 다음과 같이 찍습니다.
1번 수포자가 찍는 방식: 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, ...
2번 수포자가 찍는 방식: 2, 1, 2, 3, 2, 4, 2, 5, 2, 1, 2, 3, 2, 4, 2, 5, ...
3번 수포자가 찍는 방식: 3, 3, 1, 1, 2, 2, 4, 4, 5, 5, 3, 3, 1, 1, 2, 2, 4, 4, 5, 5, ...
1번 문제부터 마지막 문제까지의 정답이 순서대로 들은 배열 answers가 주어졌을 때, 가장 많은 문제를 맞힌 사람이 누구인지 배열에 담아 return 하도록 solution 함수를 작성해주세요.
— 제한 조건
— 입출력 예
answers | return |
---|---|
[1,2,3,4,5] | [1] |
[1,3,2,4,2] | [1,2,3] |
입출력 예 #1
따라서 가장 문제를 많이 맞힌 사람은 수포자 1입니다.
입출력 예 #2
— 문제 풀이
import java.util.*;
class Solution {
/*
수포자 1 : 1 2 3 4 5 ...
수포자 2 : 2 1 2 3 2 4 2 5 ...
수포자 3 : 3 3 1 1 2 2 4 4 5 5 ...
*/
public int[] solution(int[] answers) {
int[] method1 = {1,2,3,4,5};
int[] method2 = {2,1,2,3,2,4,2,5};
int[] method3 = {3,3,1,1,2,2,4,4,5,5};
int[] cnt = new int[3+1]; // 수포자 3명 맞춘 갯수 cnt
int best = 0;
for(int i=0;i<answers.length;i++){
for(int j=1;j<=3;j++){
if(j==1 && answers[i] == method1[i%5]){
cnt[j]++;
}else if (j==2 && answers[i] == method2[i%8]){
cnt[j]++;
}else if (j==3 && answers[i] == method3[i%10]){
cnt[j]++;
}
if(best<cnt[j]) best++;
}
}
ArrayList<Integer>list = new ArrayList<>();
for(int i=1;i<=3;i++){
if(best==cnt[i]) list.add(i);
}
int[] answer = new int[list.size()];
for(int i=0;i<answer.length;i++){
answer[i] = list.get(i);
}
return answer;
}
}
https://school.programmers.co.kr/learn/courses/30/lessons/12977
— 문제 설명
주어진 숫자 중 3개의 수를 더했을 때 소수가 되는 경우의 개수를 구하려고 합니다. 숫자들이 들어있는 배열 nums가 매개변수로 주어질 때, nums에 있는 숫자들 중 서로 다른 3개를 골라 더했을 때 소수가 되는 경우의 개수를 return 하도록 solution 함수를 완성해주세요.
— 제한 조건
— 입출력 예
nums | result |
---|---|
[1,2,3,4] | 1 |
[1,2,7,6,4] | 4 |
입출력 예 #1
[1,2,4]를 이용해서 7을 만들 수 있습니다.
입출력 예 #2
[1,2,4]를 이용해서 7을 만들 수 있습니다.
[1,4,6]을 이용해서 11을 만들 수 있습니다.
[2,4,7]을 이용해서 13을 만들 수 있습니다.
[4,6,7]을 이용해서 17을 만들 수 있습니다.
— 문제 풀이
class Solution {
public int cnt = 0;
public int solution(int[] nums) {
cal(nums,0,0,0);
return cnt;
}
public void cal(int[] nums, int cur, int pick, int sum){
if(pick == 3){
if(isPrime(sum))cnt++;
}else{
for(int i=cur;i<nums.length;i++){
cal(nums, i+1, pick+1, sum+nums[i]);
}
}
}
public boolean isPrime(int n){
if(n==0 || n==1) return false;
for(int i=2;i<=Math.sqrt(n);i++){
if(n%i==0) return false;
}
return true;
}
}
@ElementCollection
@CollectionTable(name = "order_items", joinColumns = @JoinColumn(name = "order_id"))
@Column(name = "order_item_id")
private List<Long> orderItemIds;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private List<OrderProduct> orderProducts = new ArrayList<>();
@ManyToOne
@JoinColumn(name = "order_id")
private Order order;
public void addOrderProducts(OrderProduct orderProduct){
orderProducts.add(orderProduct);
orderProduct.setOrder(this);
}
public void updateOrder(String name, List<Long> orderProductIds, OrderStatus status, String userId) {
this.name = name;
this.orderProducts.clear();
for(Long productId : orderProductIds){
this.addOrderProducts(OrderProduct.builder().productId(productId).build());
}
this.status = status;
this.updatedBy = userId;
}
public List<ProductResDto> getAllProducts(){
try{
return productRepository.findAll()
.stream().filter(p -> p.getDeletedAt() == null).map(Product::toResDto).toList();
}catch (Exception e){
log.error(e.getMessage());
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Internal server error");
}
}
@Transactional
public void updateQuantity(Long productId, Integer quantity, String userId) {
Product product = productRepository.findById(productId)
.filter(p -> p.getDeletedAt() == null)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Product not found or has been deleted"));
if(product.getQuantity() + quantity < 0){
throw new IllegalArgumentException("Not Enough Product");
}
product.updateProduct(product.getName(), product.getDescription(),
product.getPrice(), product.getQuantity() + quantity, userId);
}
@Transactional
public void deleteOrder(Long orderId, String userId) {
Order order = orderRepository.findById(orderId)
.filter(o -> o.getDeletedAt() == null)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Order not found or has been deleted"));
// 주문 취소 때문에 수량 다시 +1
for(OrderProduct product : order.getOrderProducts()){
productClient.updateQuantity(product.getProductId(),1, userId);
}
order.setDeleted(userId);
}
implementation 'io.github.openfeign:feign-micrometer'
implementation 'io.micrometer:micrometer-tracing-bridge-brave'
implementation 'io.zipkin.reporter2:zipkin-reporter-brave'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
management:
zipkin:
tracing:
endpoint: "http://{zipkins host}:{port}/api/v2/spans"
tracing:
sampling:
probability: 1.0
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
spring:
data:
redis:
host: localhost
port: 6379
password: "!@34"
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public RedisCacheManager cacheManager(
RedisConnectionFactory connectionFactory
) {
RedisCacheConfiguration config = RedisCacheConfiguration
.defaultCacheConfig()
.disableCachingNullValues()
.entryTtl(Duration.ofSeconds(60))
.computePrefixWith(CacheKeyPrefix.simple())
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json())
);
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.build();
}
}
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, ProductResDto> productRedisTemplate(
RedisConnectionFactory redisConnectionFactory
){
RedisTemplate<String, ProductResDto> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(RedisSerializer.json());
return template;
}
}
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class OrderService {
private final OrderRepository orderRepository;
private final ProductClient productClient;
private final RedisTemplate<String, ProductResDto> productRedisTemplate;
private final String PRODUCT_CACHE_PREFIX = "productCache::";
@CachePut(cacheNames = "orderCache", key = "#result.orderId")
@CacheEvict(cacheNames = "allOrdersCache", allEntries = true)
@Transactional
public OrderResDto createOrder(OrderReqDto requestDto, String userId){
for( Long productId : requestDto.getOrderProductIds()){
ProductResDto product = productClient.getProductById(productId);
if(product == null || product.getQuantity() < 1){
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Product is sold out");
}
}
// product 수량 변경 되었으므로 캐시 삭제
for(Long productId : requestDto.getOrderProductIds()){
productClient.updateQuantity(productId,-1, userId);
productRedisTemplate.delete(PRODUCT_CACHE_PREFIX + productId);
}
Order order = requestDto.toEntity();
for(Long productId : requestDto.getOrderProductIds()){
order.addOrderProducts(OrderProduct.builder().productId(productId).build());
}
order.setCreated(userId);
return orderRepository.save(order).toResDto();
}
@Cacheable(cacheNames = "orderCache", key = "args[0]")
public OrderResDto getOrderById(Long orderId){
Order order = orderRepository.findById(orderId)
.filter(o -> o.getDeletedAt() == null)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Order not found or has been deleted"));
return order.toResDto();
}
@CachePut(cacheNames = "orderCache", key = "args[0]")
@Caching(
evict = {
@CacheEvict(cacheNames = "allOrdersCache", allEntries = true),
@CacheEvict(cacheNames = "allProductsCache", allEntries = true)
}
)
@Transactional
public OrderResDto updateOrder(Long orderId, OrderReqDto requestDto, String userId) {
Order order = orderRepository.findById(orderId)
.filter(o -> o.getDeletedAt() == null)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Order not found or has been deleted"));
Set<Long> productsIdSet = new HashSet<>(); // 수량이 변경된 product를 담을 Set
// 주문 상품 리스트를 새로 업뎃 예정이기 때문에 수량 다시 +1
for(OrderProduct product : order.getOrderProducts()){
productClient.updateQuantity(product.getProductId(),1, userId);
productsIdSet.add(product.getProductId());
}
// 업데이트 된 주문 상품 리스트 수량 -1
for(Long productId : requestDto.getOrderProductIds()){
productClient.updateQuantity(productId,-1, userId);
productsIdSet.add(productId);
}
// product 수량 변경 되었으므로 캐시 삭제
for(Long productId : productsIdSet){
productRedisTemplate.delete(PRODUCT_CACHE_PREFIX + productId);
}
order.updateOrder(requestDto.getName(), requestDto.getOrderProductIds(), OrderStatus.valueOf(requestDto.getStatus()) ,userId);
return order.toResDto();
}
@Caching(
evict = {
@CacheEvict(cacheNames = "allOrdersCache", allEntries = true),
@CacheEvict(cacheNames = "orderCache", key = "args[0]")
}
)
@Transactional
public void deleteOrder(Long orderId, String userId) {
Order order = orderRepository.findById(orderId)
.filter(o -> o.getDeletedAt() == null)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Order not found or has been deleted"));
// 주문 취소 때문에 수량 다시 +1
// product 수량 변경 되었으므로 캐시 삭제
for(OrderProduct product : order.getOrderProducts()){
productClient.updateQuantity(product.getProductId(),1, userId);
productRedisTemplate.delete(PRODUCT_CACHE_PREFIX + product.getProductId());
}
order.setDeleted(userId);
}
}
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public RedisCacheManager cacheManager(
RedisConnectionFactory connectionFactory
) {
RedisCacheConfiguration config = RedisCacheConfiguration
.defaultCacheConfig()
.disableCachingNullValues()
.entryTtl(Duration.ofSeconds(60))
.computePrefixWith(CacheKeyPrefix.simple())
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(
RedisSerializer.java()
)
);
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.build();
}
}
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class ProductResDto implements Serializable { // implements Serializable 추가
private Long id;
private String name;
private String description;
private Integer price;
private Integer quantity;
}
@Service
@RequiredArgsConstructor
@Slf4j(topic = "Product Service")
@Transactional(readOnly = true)
public class ProductService {
private final ProductRepository productRepository;
@CachePut(cacheNames = "productCache", key = "#result.id")
@CacheEvict(cacheNames = "allProductsCache", allEntries = true)
@Transactional
public ProductResDto createProduct(ProductReqDto requestDto, String userId) {
Product product = requestDto.toEntity();
product.setCreated(userId);
return productRepository.save(product).toResDto();
}
@CachePut(cacheNames = "productCache", key = "args[0]")
@CacheEvict(cacheNames = "allProductsCache", allEntries = true)
@Transactional
public ProductResDto updateProduct(Long productId,ProductReqDto requestDto, String userId) {
Product product = productRepository.findById(productId)
.filter(p -> p.getDeletedAt() == null)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Product not found or has been deleted"));
product.updateProduct(requestDto.getName(), requestDto.getDescription(),
requestDto.getPrice(), requestDto.getQuantity(), userId);
return product.toResDto();
}
@Caching(
evict = {
@CacheEvict(cacheNames = "allProductsCache", allEntries = true),
@CacheEvict(cacheNames = "productCache", key = "args[0]")
}
)
@Transactional
public void deleteProduct(Long productId, String userId) {
Product product = productRepository.findById(productId)
.filter(p -> p.getDeletedAt() == null)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Product not found or has been deleted"));
product.setDeleted(userId);
}
@Cacheable(cacheNames = "allProductsCache", key = "methodName")
public List<ProductResDto> getAllProducts(){
try{
return productRepository.findAll()
.stream().filter(p -> p.getDeletedAt() == null).map(Product::toResDto).toList();
}catch (Exception e){
log.error(e.getMessage());
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Internal server error");
}
}
@Cacheable(cacheNames = "productCache", key = "args[0]")
public ProductResDto getProductById(Long productId){
Product product = productRepository.findById(productId)
.filter(p -> p.getDeletedAt() == null)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Product not found or has been deleted"));
return product.toResDto();
}
@CachePut(cacheNames = "productCache", key = "#result.id")
@CacheEvict(cacheNames = "allProductsCache", allEntries = true)
@Transactional
public ProductResDto updateQuantity(Long productId, Integer quantity, String userId) {
Product product = productRepository.findById(productId)
.filter(p -> p.getDeletedAt() == null)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Product not found or has been deleted"));
if(product.getQuantity() + quantity < 0){
throw new IllegalArgumentException("Not Enough Product");
}
product.updateProduct(product.getName(), product.getDescription(),
product.getPrice(), product.getQuantity() + quantity, userId);
return product.toResDto();
}
}
aws --version
aws cli 설치 확인aws configure
aws cli 계정 설정aws sts get-caller-identity
설정 완료 시 UserId, Account, Arn 정보 확인 가능aws ecr get-login-password --region {Region} | docker login --username AWS --password-stdin {AWS UserId}.dkr.ecr.{Region}.amazonaws.com
설정으로 ECR 사용 가능# 예시
docker tag {빌드 한 이미지}:{빌드한 이미지 태그} {ECR URL}:{이미지 태그}
# 예시
docker push {ECR URL}:{이미지 태그}
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
AWS_REGION
DB_PW
NAVER_SECRET
# This workflow will build and push a new container image to Amazon ECR,
# and then will deploy a new task definition to Amazon ECS, when there is a push to the "master" branch.
#
# To use this workflow, you will need to complete the following set-up steps:
#
# 1. Create an ECR repository to store your images.
# For example: `aws ecr create-repository --repository-name my-ecr-repo --region us-east-2`.
# Replace the value of the `ECR_REPOSITORY` environment variable in the workflow below with your repository's name.
# Replace the value of the `AWS_REGION` environment variable in the workflow below with your repository's region.
#
# 2. Create an ECS task definition, an ECS cluster, and an ECS service.
# For example, follow the Getting Started guide on the ECS console:
# https://us-east-2.console.aws.amazon.com/ecs/home?region=us-east-2#/firstRun
# Replace the value of the `ECS_SERVICE` environment variable in the workflow below with the name you set for the Amazon ECS service.
# Replace the value of the `ECS_CLUSTER` environment variable in the workflow below with the name you set for the cluster.
#
# 3. Store your ECS task definition as a JSON file in your repository.
# The format should follow the output of `aws ecs register-task-definition --generate-cli-skeleton`.
# Replace the value of the `ECS_TASK_DEFINITION` environment variable in the workflow below with the path to the JSON file.
# Replace the value of the `CONTAINER_NAME` environment variable in the workflow below with the name of the container
# in the `containerDefinitions` section of the task definition.
#
# 4. Store an IAM user access key in GitHub Actions secrets named `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`.
# See the documentation for each action used below for the recommended IAM policies for this IAM user,
# and best practices on handling the access key credentials.
name: Deploy to Amazon ECS
on:
push:
branches: [ 'action-test' ]
env:
AWS_REGION: ap-northeast-2 # set this to your preferred AWS region, e.g. us-west-1
ECR_REPOSITORY: sparta-cicd-test-lucas # set this to your Amazon ECR repository name
ECS_CLUSTER: sparta-ecs-lucas # set this to your Amazon ECS cluster name
ECS_SERVICE_NAME: sparta-ecs-service-lucas
ECS_TASK_DEFINITION: task-definition.json # set this to the path to your Amazon ECS task definition
# file, e.g. .aws/task-definition.json
CONTAINER_NAME: myselectshop-lucas # set this to the name of the container in the
# containerDefinitions section of your task definition
permissions:
contents: read
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
environment: production
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build, tag, and push image to Amazon ECR
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ github.sha }}
run: |
# Build a docker container and
# push it to ECR so that it can
# be deployed to ECS.
docker build --build-arg DBPW=${{ secrets.DB_PW }} --build-arg NAVERSECRET=${{ secrets.NAVER_SECRET }} -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
- name: Fill in the new image ID in the Amazon ECS task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: ${{ env.ECS_TASK_DEFINITION }}
container-name: ${{ env.CONTAINER_NAME }}
image: ${{ steps.build-image.outputs.image }}
- name: Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
service: ${{ env.ECS_SERVICE }}
cluster: ${{ env.ECS_CLUSTER }}
wait-for-service-stability: true
- 고객 주문 시 예상치 못한 오류로 인해 상품 서비스에서 재고 차감 하였으나, 주문 서비스에서 에러가 발생해 주문이 정상적으로 완료되지 않았을 경우 어떻게 해야 할까?
- 마이크로서비스 간의 통신에 대한 보안을 어떻게 보장 해야할까?