ListView는 가로(수평적)로 사용했을 때, 부모의 높이를 전부 사용한다. 근데, 부모가 Column위젯일 경우 에러가 나는데, 그 이유는 Column은 계속해서 수직으로 확장하려고 한다. 따라서 Column이 높이가 정해져 있지 않으니, 본인이 정해져 있는 높이가 없어서 그렇다.
Wrap 위젯
Column 위젯의 단점은, 수직으로 계속 넣기 때문인데, 최대 넓이에 도달함과 관계없이 커지기 때문에, 방지턱(오버플로우)가 일어난다. 그렇다면 최대 넓이가 도달하면 자동으로 다음 줄로 넘기고 싶을 때 사용한다.
Wrap위젯은 children 안에 자식들을 넣어주면 된다. spacing 속성값을 통해 자식들 사이 간격을 정한다. runSpacing 속성 값을 통해 줄단위 사이 간격을 정한다.
제가 디버거 모드를 이용하면서 Step Into, Resume 등 헷갈리는 기능도 있고 디버거를 잘 활용하고 싶어 작성하게 되었습니다. 또한 디버거 이용하는 방법에 잘 모르는 개발자 분들을 위해 작성하게 되었습니다.
1. Break Point
47번 줄, 48번 줄을 보면 빨간색 점이 찍혀있는 것을 볼 수 있다. 이는 Break point로 디버거 모드로 실행시켰을 때 코드 실행이 멈추는 지점
2. 디버거 모드로 실행시키는 방법
프로젝트 화면 상단에 실행시키는 버튼 옆에 벌레같이 생긴 버튼을 누르면 디버거 모드로 실행된다.
3. Step Over
Step Over는 코드 다음 줄로 넘어간다. method call 안으로 들어가지 않는다.
4. Step Into
지금 대기하고 있는 method call 내부로 들어간다.
Step Into를 누르면 아래의 화면과 같이 색이 나오는데 이는 색이 나오는 곳 중 어디로 들어갈 것인지 나타내는 색이다.
어떤 method call 안으로 들어갈 것인지 선택하면 된다.
5. Step Out
Step Into의 반대 개념으로 들어간 스택에서 나오는 기능이다. 근데 나오는데 그 스택을 실행을 시키고 빠져나온다.
6. Drop Frame
Step Out과 유사한 기능으로 그 스택을 실행시키지 않고 빠져나온다.
7. Resume Program
다음 Break Point로 이동한다.
8. Run to Cursor
Break Point를 찍고 Resume 버튼으로 다음 Break Point로 이동을 하였는데 Run to Cursor를 이용하면 Break Point를 찍지 않고 커서를 이용해 커서를 클릭하는 위치로 이동한다.
9. Condition
예를 들어 Step Into를 사용하면 for문이 50번까지 돌 때 49번째의 값을 확인하고 싶으면 Step Into를 49번 실행시켜야 한다. 하지만 Break Point에 우클릭을 해서 Condition에 확인하고 싶은 값의 Boolean 형태로 값을 넣는다. 그러면 해당하는 for문의 값만 실행시킬 수 있다.
10. Evaluate Expression
멈춘 지점에서 이런저런 값을 계산해 볼 수 있다.
Code fragment에서 실행하고 싶은 코드를 작성하고 Evaluate를 누르면 실제 콘솔에서 실행된 것을 확인할 수 있다.
container의 기본 clip behavior를 생각하자. 자식은, 부모의 상황을 전부 알지 못한다. 특히, 부모의 모양을 모르기 때문에, Container의 모양에 맞게 범위에 벗어날 경우를 대비하여 자식이 범위 밖을 지나면 자동으로 잘라주는 clipBehavior를 설정할 수 있다.
ListView 더 알기
우리에게 스크롤 기능을 주는 ListView는 수직으로만 사용했는데, 우리가 주는 속성 값에 따라 수평으로도 사용이 가능하다. 기본값의 스크롤 방향이 수직으로 설정돼있던 것이다.
중심, 회전축의 뜻을 가지고 있는 Axis, ListView에는 scrollDirection 속성을 설정할 수 있는데, 요구되는 데이터 타입이 Axis이다.
ListView의 속성 중 scrollDirection의 기본값은 Axis.vertical 이기 때문에 수직 스크롤이 되는 것이다.
Q. 그러면, Axis.vertical만 Axis.horizontal로 바꿔주면 되는 것 아닌가요?
맞지만, 하나 더 해줘야 할 것이 있습니다. ListView의 특성상, 부모 위젯의 영향을 받기 때문에, 부모를 하나 만들어 사이즈를 정해주어야 합니다. ListView를 Axis.horizontal을 하게 되면, 높이가 무제한이 되기 때문에 사이즈 부모를 꼭 넣어줘야 합니다.
프로그램 동작시 발생하는 모든 일을 기록하는 행위이다. 모든 일이란 서비스 동작 상태와 장애로 나눌 수 있는데 서비스 동작 상태에는 시스템 로딩, HTTP 통신, 트렌젝션, DB 요청, 의도를 가진 Exception 등이 있고 장애(exception, error)로는 I/O Exception, NullPointException, 의도하지 않은 Exception 등이 있다.
로깅은 언제 할까?
프로젝트 성격에 맞게, 팀에 맞게 진행한다. 따라서 로깅 시점은 때에 따라 다르다.
로깅을 어떻게 해?
System.out.println("로깅로깅")
System.err.println("에러로깅")
로깅 프레임워크를 사용
로깅 프레임워크
SLF4J
Logback
Lof4j
nlog 등
로깅 vs System.out.println()
출력 형식을 지정할 수 있음
로그 레벨에 따라 남기고 싶은 로그를 별도로 지정할 수 있음
콘솔뿐만 아니라 파일이나, 네트워크 등 로그를 별도에 위치에 남길 수 있다.
log 성능이 System.out 보다도 좋다고 한다.
로그 레벨 관련
Logback 은 5단계의 로그 레벨을 가진다.
심각도 수준은 Error > Warn > Info > Debug > Trace이다.
레벨
설명
Fatal
매우 심각한 에러, 프로그램이 종료되는경우가 많아 거의 사용되지 않음(logback 에는 fatal 설정이 아예 존재하지 않고, error 에 맵핑됩니다.)
Error
의도하지 않은 에러가 발생한 경우프로그램이 종료되지 않음프로그램 내에서 개발자가 의도하지 않은 예외를 나타날 때 사용
Warn
에러가 될 수 있는 잠재적 가능성이 있는 경우Warn 로그가 발생했을 시, 알람을 통해 개발자가 크리티컬한 에러를 맞닥뜨리기 전에 확인할 수 있는 역할을 겸함
Info
명확한 의도가 있는 정보성 로그요구사항에 따라 시스템 동작을 보여줄 떄
Debug
Info 레벨보다 더 자세한 정보가 필요한 경우주로 Develop 환경에서 사용
Trace
Debug 레벨보다 더 자세한 내용을 포함Dev 환경에서 버그를 해결하기위해 사용최종 프로덕션이나 커밋에 포함되면 안된다고 합니다.
회원가입 시, DB에 동일한 email을 가진 회원이 있을 때, DuplicationException을 던진다면 이 이벤트의 로그는 어떤 레벨을 적용할까? → Info
로깅 vs 디버깅
프로그래밍의 절반은 디버깅이다.
디버깅할 수 없는 상황에서는 로깅이 최선의 선택
디버깅을 쓸 수 있다면 디버깅을 최대한 활용
SLF4J
Simple Logging Facade for Java
다양한 로깅 프레임 워크(java.util.logging, logback 및 log4j)에 대한 추상화(인터페이스) 역할을 하는 라이브러리입니다.
인터페이스이기 때문에 단독으로 사용이 불가능합니다.
최종 사용자가 배포 시 원하는 구현체(logback, log4j 등)를 선택하여 사용 가능하다.
SLF4J 동작 과정
개발할 때, SLF4J API를 사용하여 로깅 코드를 작성
배포할 때, 바인딩된 Logging Framework가 실제 로깅 코드를 수행
Bridge
다른 로깅(SLF4J 이외의)의 API로의 Logger 호출을 SLF4J 인터페이스로 연결(redirect)하여 SLF4J API가 대신 처리할 수 있도록 하는 라이브러리이다.
이전의 레거시 로깅 프레임워크를 위한 라이브러리이다.
Bridge는 여러 개를 사용해도 상관없지만, 사용 시 주의점은 Bridge와 Binder에 같은 종류의 프레임워크를 사용하면 안 된다.
SLF4J API(인터페이스)
로깅에 대한 추상 레이어(인터페이스)를 제공한다. 즉 로깅 동작에 대한 역할을 수행할 추상 메서드를 제공한다. 앞서 말했듯이 추상 클래스이기 때문에 이 라이브러리만 단독적으로 쓰일 수 없다. 사용 시 주의점은 반드시 하나의 API에 하나의 Binding을 둬야 한다.
SLF4J Binding(.jar)
SLF4J 인터페이스를 로깅 구현체(Logging Framework)와 연결하는 어댑터 역할을 하는 라이브러리이다. SLF4J API를 구현한 클래스에서 Binding으로 연결될 Logger의 API를 호출해요. SLF4J API의 주의점과 같이 하나에 API에 하나의 Binding을 둬야 한다.
slf4j는 기본적으로 debug가 default 값으로 잡혀있어 trace 로그를 띄어주기 위해서는 별도의 설정이 필요하다고 합니다.
logging:
level:
org:
springframework: trace
application.yml 파일에 위의 코드를 추가해주어야 한다.
Logback
logback은 로깅 프레임워크 중 하나로 SLF4J의 구현체 라이브러리이다.
Log4J를 토대로 만든 프레임워크로 현재 Spring framework에서도 Slf4j 와 logback을 기본 라이브러리로 채택하고 있다.
Logback 구조
logback-core
logback-classic, logback-access 두 모듈의 기반 역할을 하는 모듈이며 Appender, Layout 인터페이스가 이 모듈에 속해 있습니다.
logback-classic
logback-core를 가지며 slf4j API를 구현하여, log4j 또는 java.util.logging(JUL)과 같은 다른 로깅 프레임워크 간에 쉽게 전환 가능하도록 하며 Logger 클래스가 이 모듈에 속해 있습니다.
logback-access
Servlet Container와 통합되어 Tomcat 및 jetty 등 Servket 컨테이너에 HTTP Access 로그 기능을 제공합니다. 웹 애플리케이션 레벨이 아닌 컨테이너 레벨에서 설치되어야 합니다.
logback vs logback-spring
스프링 공식문서를 살펴보면 가능하다면 표준보다 "-spring"을 붙여서 사용하는 걸 추천한다고 합니다.
표준 버전 즉, logback.xml을 사용한다면 Spring 이 완벽하게 로그 초기화를 제어하지 못한다고 한다.
아마도, logback.xml 파일은 너무 빨리 로드되기 때문에, 이 파일 안에선 Extensions을 사용할 수 없다고 하는데 이 때문인 것 같습니다.
로그 백 설정 파일은 logback-spring.xml 파일을 src/main/resources/logback-spring.xml 경로에 만들면서 시작합니다.
로그 파일 작성하기
콘솔 로그의 수준을 변경하는 방법은 application.yml과 logback-spring.xml 에서 설정하는 방법이 있다. application.yml 은 설정하는 난이도가 비교적 쉽지만, 실제 제품에 사용하기엔 한계가 있고 세부적인 설정이 불편하기 때문에 logback-spring.xml로 관리하는 편이 더 좋다고 한다.
logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 여기애 로그 설정 부분이 들어간다. -->
</configuration>
logback-spring.xml 구성 요소
appender는 로그가 출력되는 위치를 나타냅니다.
XXXAppender로 끝나는 클래스들이 존재하며 다양한 로그 출력 위치 및 방법을 제공합니다.
ConsoleAppender는 콘솔에 System.out 또는 System.err를 이용하여 로그 이벤트를 append 합니다.
FileAppender는 파일에 로그를 저장한다. 최대 보관 일 수 등을 지정할 수 있다.
RollingFileAppender : 여러 개의 파일을 롤링, 순회하면서 로그를 찍는다.(FileAppender를 상속받는다. 지정 용량이 넘어간 Log File을 넘버링하여 나누어 저장할 수 있다.)
SMTPAppender : 로그를 메일로 보낸다.
DBAppender: DB(데이터베이스)에 로그를 쌓는다.
layout (encoder)
logback 버전 0.9.19 이후 에로 로그의 모든 wirte 권한을 제어할 수 있는 encoder 등장하였습니다.
Encoder는 로그 이벤트를 바이트 배열로 변환하고, 해당 바이트 배열을 OutputStream에 쓰이는 작업을 담당합니다.
이전 버전에서 appender는 이벤트 메시지를 문자열로 변환하는데 layout을, write 하는데 java.io.Writer를 사용해 왔습니다.
<property name="LOG_DIR" value="/var/log/was"/>
<springProfile name="prod">
<appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="FILE">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %highlight(%-5level) %logger{0} - %msg%n</pattern>
</encoder>
<file>${LOG_DIR}/app.log</file> <!-- 파일을 저장할 경로를 정한다 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch> <!-- 해당 레벨만 기록한다. -->
<onMismatch>DENY</onMismatch> <!-- 다른 수준의 레벨은 기록하지 않는다.(상위 레벨도 기록 안함), 상위 수준의 레벨에 대한 기록을 원하면 ACCEPT 로 하면 기록된다. -->
</filter> <!-- 레벨별 필터링이 필요없을 경우 filter class 관련된 부분을 삭제하면 됨-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}-%d{yyyy-MM-dd}-%i-log.zip</fileNamePattern>
<maxFileSize>100MB</maxFileSize> <!-- 한 파일의 최대 용량(분할 용량) -->
<maxHistory>90</maxHistory> <!-- 한 파일의 최대 저장 기한 -->
<totalSizeCap>3GB</totalSizeCap> <!-- 전체 파일의 크기를 제어하며, 전체 크기 제한을 초과하면 가장 오래된파일은 삭제한다. totalSizeCap을 사용하려면 maxHistory가 필수 속성이다. -->
</rollingPolicy>
</appender>
</springProfile>
maxFileSize: 한 파일의 최대 용량(분할 용량)이다.
maxHistory: 한 파일의 최대 저장 기한 롤 오버 지정에 따라 maxHistory 기간이 정해진다.
totalSizeCap: 전체 파일의 크기를 제어하며, 전체 크기 제한을 초과하면 가장 오래된 파일은 삭제한다. totalSizeCap을 사용하려면 maxHistory가 필수 속성이다.
Logback에서 DBAppender는 독립적인 형식으로 logging_event, logging_event_property, logging_event_exception 세 개의 데이터 베이스 테이블을 insert 하므로 DBAppender를 사용하기 전에 먼저 테이블을 생성해야 합니다.
DROP TABLE IF EXISTS logging_event_property;
DROP TABLE IF EXISTS logging_event_exception;
DROP TABLE IF EXISTS logging_event;
CREATE TABLE logging_event
(
timestmp BIGINT NOT NULL,
formatted_message TEXT NOT NULL,
logger_name VARCHAR(254) NOT NULL,
level_string VARCHAR(254) NOT NULL,
thread_name VARCHAR(254),
reference_flag SMALLINT,
arg0 VARCHAR(254),
arg1 VARCHAR(254),
arg2 VARCHAR(254),
arg3 VARCHAR(254),
caller_filename VARCHAR(254) NOT NULL,
caller_class VARCHAR(254) NOT NULL,
caller_method VARCHAR(254) NOT NULL,
caller_line CHAR(4) NOT NULL,
event_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY
);
CREATE TABLE logging_event_property
(
event_id BIGINT NOT NULL,
mapped_key VARCHAR(254) NOT NULL,
mapped_value TEXT,
PRIMARY KEY(event_id, mapped_key),
FOREIGN KEY (event_id) REFERENCES logging_event(event_id)
);
CREATE TABLE logging_event_exception
(
event_id BIGINT NOT NULL,
i SMALLINT NOT NULL,
trace_line VARCHAR(254) NOT NULL,
PRIMARY KEY(event_id, i),
FOREIGN KEY (event_id) REFERENCES logging_event(event_id)
);
DBAppender와 테이블을 생성했다면 해당 테이블에 로그가 자동으로 insert 됩니다.
프로젝트 상단에 .github 폴더를 만들고 PULL_REQUEST_TEMPLATE.md 파일의 이름으로 만든다.
Edit new file을 누르고 템플릿으로 사용할 틀을 만들어 준다.
## 요약
<br><br>
## 작업 내용
<br><br>
## 참고 사항
<br><br>
## 관련 이슈
- Close #이슈번호
<br><br>
Issue template 만들기
ISSUE_TEMPLATE 폴더를 만든다. github에서 폴더 만드는 법은 file 이름 넣는 칸에 폴더 이름을 넣고 / 를 입력하면 폴더가 생긴다. 그 전으로 돌아가려면 .. 을 입력하면 된다.
위의 화면은 Issue template 작성하는 코드이다.
---
name: Bug template
about: 버그 발생 시 사용하는 템플릿입니다
title: "버그 리포트"
labels: "\\U0001F41B BUG"
assignees:
---
# 버그 리포트
## 어떤 버그인가요?
> 버그에 대해 알려주세요
<br><br>
## 어떤 상황에서 겪으셨나요?
> 버그를 겪으신 상황을 알려주세요
<br><br>
## 참고할만한 자료가 있을까요?
> 참고자료가 있다면 첨부해주세요
<br><br>
Pull Request 템플릿 작성되는지 테스트
PR 작성하는 식으로 Compare & request버튼을 누른다.
Pull Request 템플릿으로 작성했던 코드가 잘 불러와지는 것을 확인할 수 있다.
ISSUE 템플릿 확인
New Issue를 누른다.
Pull Request와는 다르게 템플릿 선택하는 화면이 나온다. Get started를 누르면 템플릿이 잘 불러와진다.