[우아한테크코스] 8월 21일 TIL

3 minute read

Sprint 2 (7/20 ~ 8/2)

[Spring] logging

프로그램 실행 중 발생하는 오류를 모니터링하기 위해 발생하는 에러를 디버깅에만 찍히는 것이 아니라 파일로 기록하도록 하였다.

배포환경과 로컬환경의 설정을 다르게 하여 배포환경에서는 400번 에러를 Info로 관리해 따로 로그 파일을 생성하지 않고, 500번 에러를 warn로 로그 파일을 생성하도록 하였고, 로컬환경에서는 디버깅에 찍히도록 하였다.

  • SLF4J

    Log4J, Logback과 같은 Logging Framework의 facade pattern

    java.util.logging, logback, log4j와 같은 로깅 프레임 워크에 대한 인터페이스 역할을 하는 라이브러리

    개발자는 SLF4J api를 이용해 로깅 코드를 작성하고 배포할 때 바인딩된 framework가 로깅 코드를 수행함으로써 로깅이 된다.

    SLF4J는 Bridging, API, Biniding 모듈 제공을 통해 컴파일 시점에 로깅 구현체를 결정하도록 한다. 스프링의 기본은 Logback이다.

    logback-test.xml -> logback.groovy -> logback.xml -> META-INF\services\ch.qos.logback.classic.spi.Configurator -> BasicConfigurator 설정

    의존성. 구현체 추가

    dependencies { 
        implementation 'org.slf4j:slf4j-api:1.7.31' 
        mplementation 'ch.qos.logback:logback-core:1.2.3' 
        implementation ('ch.qos.logback:logback-classic:1.2.3'){ 
            exclude group: 'org.slf4j', module: 'slf4j-api' 
            exclude group: 'ch.qos.logback', module: 'logback-core' 
          } 
      }
    

    아래는 제일 상단 설정

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <!--    Constants     -->
        <timestamp key="DATE_FORMAT"
                   datePattern="yyyy-MM-dd"/>
      
        <property name="CONSOLE_LOG_PATTERN"
                  value="%highlight([%-5level]) %d{yyyy-MM-dd HH:mm:ss} %cyan([%thread]) %magenta([%logger{0}:%line]) - %message %n"/>
      
        <property name="FILE_LOG_PATTERN"
                  value="[%-5level] %d{yyyy-MM-dd HH:mm:ss} [%thread] [%logger{0}:%line] - %message %n"/>
      
        <property name="FILE_PATH"
                  value="${user.home}/logs"/>
      
        <!--    Profiles    -->
        <springProfile name="local">
            <include resource="appenders/console-appender.xml"/>
      
            <root level="INFO">
                <appender-ref ref="CONSOLE_APPENDER" />
            </root>
        </springProfile>
      
        <springProfile name="prod">
            <include resource="appenders/console-appender.xml"/>
            <include resource="appenders/file-appender-error.xml"/>
            <include resource="appenders/file-appender-warn.xml"/>
      
            <root level="WARN">
                <appender-ref ref="CONSOLE_APPENDER" />
                <appender-ref ref="FILE_APPENDER_ERROR" />
                <appender-ref ref="FILE_APPENDER_WARN" />
            </root>
        </springProfile>
    </configuration>
    

참고-백기선님

참고-검프

[Java] svg를 png 파일로 변환

svg는 2차원 벡터 그래픽을 표현하기 위한 xml 파일 형식

jpg, png, gif 등등은 레스터 방식을 이용한다.

레스터 방식은 이미지 모양과 색을 색상 정보가 담긴 픽셀로 표현하는 방식이다.

svg는 벡터 방식으로 도형이나 선을 그려서 표시하는 방식이다.

rectangle, circle, ellipse, line, polyline, polygon과 같은 타입 지정 가능하다.

path요소를 사용해 모양을 제어해 다각형을 그릴 수 있다. path는 d 속성을 이용해 여러 개의 명령어와 파라미터들로 이뤄져 직선과 곡선을 합쳐서 복잡한 도형을 그릴 수 있다. 알파벳으로 시작하는 명령어를 통해 위치, 크기 등을 지정할 수 있다.

이 svg 코드를 png 파일로 변환하려면 Apache Batik 라이브러리를 사용할 수 있다.

    //svg
    implementation 'org.apache.xmlgraphics:batik-all:1.12'
    implementation 'org.apache.xmlgraphics:xmlgraphics-commons:2.4'
    implementation 'xml-apis:xml-apis:1.4.01'
    implementation 'xml-apis:xml-apis-ext:1.3.04'
  1. svg 코드를 파일로 변환한다.
byte[] svgByte = svg.getBytes();
String svgFileName = "/Users/yeonwoocho/Desktop/pr.svg";
OutputStream svgOutput = new FileOutputStream(svgFileName);
svgOutput.write(svgByte);
svgOutput.close();
  1. svg 이미지를 TranscoderInput으로 변환한다.
TranscoderInput inputSvg = new TranscoderInput("file:" + svgFileName);
  1. png 파일을 세팅한다.
OutputStream pngOutputStream = new FileOutputStream("/Users/yeonwoocho/Desktop/practice.png");
TranscoderOutput pngOutputImage = new TranscoderOutput(pngOutputStream);
  1. png Transcoder를 생성한다.
PNGTranscoder pngTranscoder = new PNGTranscoder();        
  1. svg를 png로 변환한다.
pngTranscoder.transcode(inputSvg, pngOutputImage);
  1. flush output stream
pngOutputStream.flush();
pngOutputStream.close(); 

[Infra] CDN

콘텐츠 전송 네트워크(Content Distribution Network)로 콘텐츠를 효율적으로 전달하기 위해 여러 노드를 가진 네트워크에 데이터를 저장해 제공하는 시스템이다.

ISP에 직접 연결되어 데이터를 전송하므로 콘텐츠 병목을 피할 수 있다.

서버와 사용자 사이의 물리적 거리를 줄여 웹 페이지 콘텐츠 로드 지연을 최소화한다.

촘촘히 분산된 서버로 이루어진 플랫폼이다.

  • 작동방법

    CDN을 사용하지 않으면 오리진 서버들은 모든 엔드유저의 요청에 일일이 응답해야 한다. 이것은 막대한 트래픽을 유발하고 엄청난 부하를 일으켜 오리진에 장애가 발생할 확률이 높다.

    웹 페이지에 대한 요청이 이동해야 하는 물리적 거리를 줄여 웹 페이지 간의 지연 시간을 줄이는 것이다.

    Points of Presence라는 전 세계 여러 지역에 캐시된 버전의 웹 사이트 콘텐츠를 저장해 캐싱된 버전으로 응답한다.

amazon에서는 CloudFront가 이 CDN이다.

CloudFront

빠른 속도로 데이터, 동영상, 어플리케이션, API를 전 세계에 안전하게 전송하는 고속 콘텐츠 전송 네트워크 서비스

Https 지원과 필드 수준 암호화를 포함한 대부분의 고급 기능을 지원하며 S3, EC2, Elastic Load Balancing, 사용자 지정 HTTP 오리진까지 적용가능하다.

[Infra] S3

보안과 성능을 제공하는 객체 스토리지 서비스로 간편하게 데이터를 관리할 수 있을 뿐만 아니라 액세스 제어도 가능하다.

원래는 MultipartFile과 Blob을 이용해 이미지를 DB에 바이너리 파일로 저장할까 했는데 프론트에서 png 파일 주소 자체를 넘겨받기를 원했다.

png파일 S3에 저장하기

  1. S3Uploader 구현

    의존성 추가

implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'

​ 프로퍼티 추가

#s3
cloud.aws.s3.bucket_name=2021-zzimkkong-thumbnail
cloud.aws.stack.auto=false  //Spring cloud 프로젝트를 실행하면 기본으로 구성되는 CloudFormation 구성 사용하지 않도록 함, 없으면 프로젝트 시작이 안됨 
cloud.aws.crecentials.instanceProfile=true //ACL로 확인한 Key들을 사용한다. AWS의 instanceProfile을 사용한다.

원래는 버킷에 접근하기 위한 key를 등록해야하지만 현상태로는 접근 권한을 가진 인스턴스 ec2-s3-deploy를 보안 정책으로 삼고 있기 때문에 필요하지 않다.

원래는 버킷 권한에 AmazonS3FullAccess를 원래는 권한 선택을 해주어야한다.

public, private key를 발급 받아서 등록해주어야 한다.

@Component
public class S3Uploader {
    private final AmazonS3 amazonS3;

    public S3Uploader(AmazonS3 amazonS3) {
        this.amazonS3 = amazonS3;
    }

    @Value("${aws.s3.bucket_name}")
    private String bucket;  //해당 버킷 주입

    public String upload(File uploadFile) {
        String fileName = "thumbnails/" + uploadFile.getName(); 
        String uploadImageUrl = putS3(uploadFile, fileName);    //S3에 저장한다
        uploadFile.delete();    //저장된 파일을 삭제하고
        return uploadImageUrl;  //해당 url을 넘겨준다
    }

    private String putS3(File uploadFile, String fileName) {
        amazonS3.putObject(new PutObjectRequest(bucket, fileName, uploadFile));
        return amazonS3.getUrl(bucket, fileName).toString();
    }
}

참고

[Infra] Git 서브모듈

git 저장소 안에 다른 git 저장소를 디렉토리로 분리해 넣는 것을 서브모듈이라고 한다.

서브모듈로 만들어진 디렉토리의 하위 내용은 기존 저장소가 관리하지 않는다.

private으로 관리되는 서브모듈을 디렉토리로 넣으면 타인이 접근해도 존재는 알지만 내부 내용은 알 수 없어 팀원들에게만 공유해야 하는 보안 관련 파일을 관리하기 좋다.

  1. 서브 저장소 만들었으면 서브모듈 등록

    git submodule add -b ${브랜치명} ${사용하고자 하는 서브 레파리토리 주소}

  2. 최상위/.gitmodules 에 추가되었는지 확인

  3. 기존 레파지토리 클론 시

    git clone --recurse-submodules ${기존 레파지토리 주소}

  4. 이미 등록된 레파지토리 사용 시

    git pull

    git submodule update --init --remote

  5. 서브모듈 최신화

    git submodule update --remote --merge

참고