반응형

 

Github 에서 소스를 clone 하는데 아래와 같이 에러가 발생했다.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/...../'

 

 

내용인즉슨...

GitHub에서 2021년 8월 13일부터 HTTPS를 통한 비밀번호 인증을 더 이상 지원하지 않는다는 것이다!!

대신 **Personal Access Token (PAT)**을 사용해야 한다고 한다!

 

 

Github 내 프로필의 설정에서 토큰을 발급 페이지

- Settings > Developer Settings

https://github.com/settings/tokens

 

GitHub · Build and ship software on a single, collaborative platform

Join the world's most widely adopted, AI-powered developer platform where millions of developers, businesses, and the largest open source community build software that advances humanity.

github.com

 

 

Generate new token(classic) 메뉴를 클릭해서 토큰을 생성

 

 

Select scopes 은 아래와 같이 설정

 

 

결론

 - 비밀번호 대신에 토큰을 입력하면 된다!!!

 

 

반응형
반응형

 

MySQL 사용시 테이블 대소문자 구분하지 않고 사용하기

 

Server version: 5.7.13

 

MySQL 설정 확인

mysql> show variables like 'lower%';
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| lower_case_file_system | OFF   |
| lower_case_table_names | 0     |
+------------------------+-------+

 

lower_case_table_names

 - 1 : 대소문자 구분하지 않고 사용.

 - 0 : 대소문자 구분하여  사용.

 

 

MySQL 설정 변경

- read ony 값이라 set 명령어로 변경할 수 없음.

mysql> set lower_case_table_names=1;
ERROR 1238 (HY000): Variable 'lower_case_table_names' is a read only variable

 

- my.cnf 파일 수정 후 재구동

$ vi /etc/my.cnf

[mysqld]
...
lower_case_table_names = 1
...

 

반응형
반응형

java 소수점 처리

 - 데이터 타입이 float 이나 double 이어야 합니다.

 

ex) 나누기 연산을 해서 소수 둘째자리까지 출력하는 방법.

1. String.format

 - 반올림 된 값으로 결과값 출력.

 - 소수점 자릿수 지정(고정)

private static void div(int mb) {
        double gb = (double)mb / 1024;
        System.out.println("\n---------------------------------");
        System.out.println("" + mb + " / 1024 = " + gb);

        System.out.println("String.format = " + String.format("%.2f", gb));
}

div(1024);
div(1540);
div(1550);
div(1400);


결과 :
---------------------------------
1024 / 1024 = 1.0
String.format = 1.00

---------------------------------
1540 / 1024 = 1.50390625
String.format = 1.50

---------------------------------
1550 / 1024 = 1.513671875
String.format = 1.51

---------------------------------
1400 / 1024 = 1.3671875
String.format = 1.37

 

2. BigDecimal

 - 반올림/올림/내림 설정 가능.

 - 소수점 자릿수 지정(고정 / 마지막이 0 인 경우 버림 가능)

private static void div(int mb) {
        double gb = (double)mb / 1024;
        System.out.println("\n---------------------------------");
        System.out.println("" + mb + " / 1024 = " + gb);

        BigDecimal bdMb = new BigDecimal(mb);
        BigDecimal bdKb = new BigDecimal(1024);
        System.out.println("BigDecimal = " + bdMb.divide(bdKb, 2, RoundingMode.DOWN));
        System.out.println("BigDecimal(stripTrailingZeros) = " + bdMb.divide(bdKb, 2, RoundingMode.DOWN).stripTrailingZeros());
}
    
div(1024);
div(1540);
div(1550);
div(1400);


---------------------------------
1024 / 1024 = 1.0
BigDecimal = 1.00
BigDecimal(stripTrailingZeros) = 1

---------------------------------
1540 / 1024 = 1.50390625
BigDecimal = 1.50
BigDecimal(stripTrailingZeros) = 1.5

---------------------------------
1550 / 1024 = 1.513671875
BigDecimal = 1.51
BigDecimal(stripTrailingZeros) = 1.51

---------------------------------
1400 / 1024 = 1.3671875
BigDecimal = 1.36
BigDecimal(stripTrailingZeros) = 1.36

 

3. DecimalFormat

 - 반올림 된 값으로 결과값 출력.

 - 소수점 자릿수 지정(고정 / 마지막이 0 인 경우 버림 가능 / 0 버림 갯수 지정 가능)

private static void div(int mb) {
        double gb = (double)mb / 1024;
        System.out.println("\n---------------------------------");
        System.out.println("" + mb + " / 1024 = " + gb);

        DecimalFormat formatter = new DecimalFormat("0.##");
        System.out.println("DecimalFormat(0.##) = " + formatter.format(gb));

        DecimalFormat formatter2 = new DecimalFormat("0.0#");
        System.out.println("DecimalFormat(0.0#) = " + formatter2.format(gb));
}

div(1024);
div(1540);
div(1550);
div(1400);


결과 :
---------------------------------
1024 / 1024 = 1.0
DecimalFormat(0.##) = 1
DecimalFormat(0.0#) = 1.0

---------------------------------
1540 / 1024 = 1.50390625
DecimalFormat(0.##) = 1.5
DecimalFormat(0.0#) = 1.5

---------------------------------
1550 / 1024 = 1.513671875
DecimalFormat(0.##) = 1.51
DecimalFormat(0.0#) = 1.51

---------------------------------
1400 / 1024 = 1.3671875
DecimalFormat(0.##) = 1.37
DecimalFormat(0.0#) = 1.37

 

4. DecimalFormat + BigDecimal

 - 반올림 된 값으로 결과값 출력.

 - 소수점 자릿수 지정(고정 / 마지막이 0 인 경우 버림 가능 / 0 버림 갯수 지정 가능)

private static void div(int mb) {
        double gb = (double)mb / 1024;
        System.out.println("\n---------------------------------");
        System.out.println("" + mb + " / 1024 = " + gb);

        DecimalFormat formatter = new DecimalFormat("0.##");
        DecimalFormat formatter2 = new DecimalFormat("0.0#");
        System.out.println("DecimalFormat(BigDecimal, 0.##) = " + formatter.format(bdMb.divide(bdKb, 2, RoundingMode.DOWN)));
        System.out.println("DecimalFormat(BigDecimal, 0.0#) = " + formatter2.format(bdMb.divide(bdKb, 2, RoundingMode.DOWN)));
}
    
div(1024);
div(1540);
div(1550);
div(1400);
        

결과 :
---------------------------------
1024 / 1024 = 1.0
DecimalFormat(BigDecimal, 0.##) = 1
DecimalFormat(BigDecimal, 0.0#) = 1.0

---------------------------------
1540 / 1024 = 1.50390625
DecimalFormat(BigDecimal, 0.##) = 1.5
DecimalFormat(BigDecimal, 0.0#) = 1.5

---------------------------------
1550 / 1024 = 1.513671875
DecimalFormat(BigDecimal, 0.##) = 1.51
DecimalFormat(BigDecimal, 0.0#) = 1.51

---------------------------------
1400 / 1024 = 1.3671875
DecimalFormat(BigDecimal, 0.##) = 1.36
DecimalFormat(BigDecimal, 0.0#) = 1.36

 

반응형
반응형

 

MySQL 사용자 생성 시 에러가 발생했습니다.

mysql> create user 'user1'@'%' identified by 'passwd1';
ERROR 1819 (HY000): Your password does not satisfy the current policy requirements

 

패스워드가 정책 요구사항에 맞지 않다는 얘기입니다.

 

 

그럼 현재 정책이 어떤지 확인을 해보겠습니다.

mysql> show variables like 'validate_password%';
+--------------------------------------+--------+
| Variable_name                        | Value  |
+--------------------------------------+--------+
| validate_password_check_user_name    | OFF    |
| validate_password_dictionary_file    |        |
| validate_password_length             | 8      |
| validate_password_mixed_case_count   | 1      |
| validate_password_number_count       | 1      |
| validate_password_policy             | MEDIUM |
| validate_password_special_char_count | 1      |
+--------------------------------------+--------+
7 rows in set (0.01 sec)

 

패스워드 정책이 MEDIUM 으로 설정되어 있는 것을 확인할 수 있습니다.

| validate_password_policy             | MEDIUM |

 

 

※ 패스워드 정책은 MySQL 문서에 따르면 이렇게 정의되어 있네요.

 

 

해결책

1. 현재 설정되어 있는 정책에 맞게 패스워드를 만든다.

mysql> create user 'user1'@'%' identified by '유저1!Pw처럼';

2. 현재 설정되어 있는 정책을 바꾼다.

# MySQL 8
mysql> set global validate_password.policy=LOW;
Query OK, 0 rows affected (0.00 sec)

# MySQL 5
mysql> set global validate_password_policy=LOW;
Query OK, 0 rows affected (0.00 sec)

 

 

반응형

'Database > Mysql' 카테고리의 다른 글

MySQL 테이블 대소문자 구분  (0) 2024.03.08
index 생성 기준?  (0) 2021.09.24
[API] 앱 버전 체크 시 사용할 만한 쿼리  (1) 2017.10.11
mysql 에서 unix time 구하기  (0) 2015.09.23
mysql bin log 삭제.  (0) 2014.05.15
반응형

 

빗버킷에 저장소(repository) 생성 후 로컬 PC 에서 git 처음 사용시 에러 발생.

$ git push -u origin master
error: src refspec master does not match any
error: failed to push some refs to 'http://git.com/test.git'

 

아마도 remote 의 master 브랜치를 인식하지 못하는 것 같다.

$ git branch -m master

 

위 처럼 master 브랜치를 설정 한 후 push 하면 잘된다.

$ git push -u origin master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 209 bytes | 209.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To http://git.com:7990/test.git
 * [new branch]      master -> master
branch 'master' set up to track 'origin/master'.

 

반응형
반응형

엑셀 파일 읽기 시 아래 에러 발생시 해결 방법입니다.

 

라이브러리 추가

apache poi 라이브러리를 추가합니다.

		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi</artifactId>
			<version>5.2.3</version>
		</dependency>

		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml</artifactId>
			<version>5.2.3</version>
		</dependency>

 

엑셀 파일 읽기 소스 코드

public static void readExcel() {
		try {
            FileInputStream file = new FileInputStream("file_path.xlsx");
            IOUtils.setByteArrayMaxOverride(Integer.MAX_VALUE);

            //Create Workbook instance holding reference to .xlsx file
            XSSFWorkbook workbook = new XSSFWorkbook(file);

            //Get first/desired sheet from the workbook
            XSSFSheet sheet = workbook.getSheetAt(0);

            //Iterate through each rows one by one
            Iterator<Row> rowIterator = sheet.iterator();
            while (rowIterator.hasNext()) {
                Row row = rowIterator.next();
                //For each row, iterate through all the columns
                Iterator<Cell> cellIterator = row.cellIterator();

                while (cellIterator.hasNext()) {
                    Cell cell = cellIterator.next();
                    //Check the cell type and format accordingly
                    switch (cell.getCellType()) {
                        case NUMERIC:
                            System.out.print(cell.getNumericCellValue() + "\t");
                            break;

                        case STRING:
                            System.out.print(cell.getStringCellValue() + "\t");
                            break;

                        case BLANK :
                        	System.out.print("\t");
                            break;

                        default:
                            throw new IllegalStateException("Unexpected value: " + cell.getCellType());
                    }
                }
                System.out.println("");
            }
            file.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
	}

 

에러 발생

 

Exception in thread "main" java.lang.NoSuchMethodError: org.apache.logging.log4j.Logger.atDebug()Lorg/apache/logging/log4j/LogBuilder;

 

 

 

해결 방법

		<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-to-slf4j -->
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-to-slf4j</artifactId>
			<version>2.17.2</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-api</artifactId>
			<version>2.17.2</version>
		</dependency>

 

반응형

'프로그래밍 > Java' 카테고리의 다른 글

java 소수점 처리  (1) 2023.10.10
Java Stream 설명  (0) 2023.05.26
Stream reduce 간단 설명  (0) 2023.05.20
java file.encoding  (0) 2022.07.12
[JAVA] java.lang.UnsatisfiedLinkError: no net in java.library.path  (1) 2021.06.25
반응형

 

Spring Boot 사용시 logback 설정에 대한 우선 순위를 알아봅니다.

 

 

Spring Properties 파일에 정의

 - application.properties

logging.level.org.springframework.boot.autoconfigure=DEBUG

 

 

Jar 실행 시 환경 변수로 정의

 - logback.xml

<logger name="org.springframework.boot.autoconfigure"><level value="INFO" /></logger>

 

우선 순위

 - Spring Properties 에서 설정한 내용이 우선 순위가 높습니다.

 - 그러나 logback.xml 에서는 실시간 변경등 작업을 할 수 있습니다.

 

 

반응형

'프로그래밍 > Spring' 카테고리의 다른 글

Spring Properties 파일 로딩 우선 순위  (1) 2023.01.04
반응형

Java Stream

 

Java Stream 특징

- Java 8부터 도입된 기능으로, 컬렉션, 배열, 파일 등의 데이터 요소를 처리하는 연산을 지원하는 함수형 Stream 입니다.

- 데이터 소스로부터 연속된 데이터 흐름을 제공하여 데이터를 처리합니다.

- 데이터를 효율적으로 처리할 수 있으며, 병렬 처리를 포함한 다양한 연산을 적용할 수 있습니다. 

- 데이터를 필터링, 매핑, 정렬, 그룹핑 등 다양한 작업을 수행할 수 있으며, 함수형 프로그래밍 스타일을 지원하여 코드를 간결하고 가독성 있게 작성할 수 있습니다.

- 중간 연산과 최종 연산으로 구성된 파이프라인을 만들 수 있습니다. 중간 연산은 다른 Stream 을 반환하며, 최종 연산은 최종 결과를 반환합니다.

 

Java Stream 처리 단계

1. 데이터 소스 생성

 - 컬렉션, 배열, 파일 등의 데이터 소스로부터 Stream 을 생성합니다.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = numbers.stream();

 

2. 중간 연산

 - 필터링, 매핑, 정렬 등의 작업을 수행할 수 있습니다.

stream = stream.filter(n -> n % 2 == 0) // 짝수만 필터링
               .map(n -> n * 2)         // 각 요소를 2배로 매핑
               .sorted();               // 정렬

 

3. 최종 연산

 - 리스트, 배열, 요소 개수 등 다양한 결과를 얻을 수 있습니다.

// 요소 개수 세기(count)
long count = numbers.stream()
                   .count();
System.out.println(count);  // 출력: 5

// 요소들의 합 구하기(sum)
int sum = numbers.stream()
                .mapToInt(Integer::intValue)
                .sum();
System.out.println(sum);  // 출력: 15


// 최대값 찾기(max)
Optional<Integer> max = numbers.stream()
                               .max(Comparator.naturalOrder());
System.out.println(max.orElse(0));  // 출력: 5


// 요소들의 평균 구하기(average)
OptionalDouble average = numbers.stream()
                               .mapToInt(Integer::intValue)
                               .average();
System.out.println(average.orElse(0));  // 출력: 3.0


// 요소들을 리스트로 변환(collect)
List<Integer> doubledNumbers = numbers.stream()
                                      .map(n -> n * 2)
                                      .collect(Collectors.toList());
System.out.println(doubledNumbers);  // 출력: [2, 4, 6, 8, 10]

※ 이외에도 최소값 찾기(min), 문자열로 연결하기(join), 그룹화하기(groupingBy) 등 다양한 최종 연산이 있습니다.

 

Java Stream VS Parallel Stream

구분 Stream Parallel Stream
데이터 처리 방식 순차적으로 연산을 수행 병렬로 연산을 수행
처리 속도 단일 CPU 코어에서 동작하며, 작업을 직렬로 처리.  멀티코어 시스템에서 데이터 처리 속도를 높일 수 있음.
작업을 분할하고 스레드 간의 동기화 오버헤드가 발생할 수 있으므로, 작업량이 많거나 데이터가 충분히 큰 경우에 이점.
스레드 안전성 - 멀티 스레드에서 작업을 수행하므로 스레드 안전성에 주의.
thread-safe 한 자료구조를 사용하거나 동기화 메커니즘을 적절하게 사용
순서 보장 연산 순서가 보장되며, 요소 순서 유지 병렬로 처리하기 때문에 요소의 순서가 보장되지 않을 수 있음. 
사용처 데이터의 순서가 중요하거나 상호작용이 필요한 경우 유용 멀티코어 CPU 시스템에서 병렬 처리에 특히 유용하며, 대량의 데이터를 동시에 처리해야 할 때 성능 향상을 제공

 

소스로 비교

public static void main(String[] args) {
    // 데이터 소스
    int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    // stream
    int sumSequential = Arrays.stream(numbers).sum();
    System.out.println("Sum (Sequential): " + sumSequential);

    // parallel stream
    int sumParallel = Arrays.stream(numbers)
        .parallel()
        .sum();
    System.out.println("Sum (Parallel): " + sumParallel);
}
    
// 결과 출력
Sum (Sequential): 55
Sum (Parallel): 55

stream : Arrays.stream(numbers)를 호출하여 스트림을 생성하고, sum() 메서드를 호출하여 모든 요소의 합을 계산

parallel stream : Arrays.stream(numbers)를 호출하여 스트림을 생성한 후 .parallel() 메서드를 호출하여 병렬 스트림으로 전환, 그리고 나서 sum() 메서드를 호출하여 병렬로 모든 요소의 합을 계산

※  합계를 구하는 간단한 작업. 작은 데이터셋이므로 parallel stream 을 사용하여도 순차 stream 과 동일한 결과.

 

parallel stream 잘못된 사용 예제

public static void main(String[] args) {
    // 데이터 소스
    int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    // 잘못된 사용 예제
    int sum = Arrays.stream(numbers)
        .parallel()
        .map(n -> {
            if (n == 5) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return n;
        })
        .sum();
        
    System.out.println("Sum: " + sum);
}

// 결과 출력
Sum: 50

 - 배열 numbers를 데이터 소스로 사용하여 parallel stream을 이용하여 요소들을 처리하고 합계(sum)를 계산

- parallel stream 내부의 map 연산에서 특정 조건(n == 5)에 따라 5인 경우 1초의 지연을 발생

- parallel stream은 요소를 병렬로 처리하기 때문에 map 연산의 각 요소는 병렬로 실행

- 조건 n == 5를 만족하는 요소가 존재할 경우 1초의 지연이 발생하고, 이로 인해 전체 연산에 대한 지연이 발생

 

parallel stream 처리 순서 확인

-public static void main(String[] args) {
    // 데이터 소스
    int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    Arrays.stream(numbers)
        .parallel()
        .map(n -> {
            System.out.println("Map: " + n + " - " + Thread.currentThread().getName());
            return n;
        })
        .forEach(n -> System.out.println("ForEach: " + n + " - " + Thread.currentThread().getName()));
-}

// 결과 출력
Map: 1 - main
Map: 2 - ForkJoinPool.commonPool-worker-1
Map: 5 - ForkJoinPool.commonPool-worker-2
Map: 4 - ForkJoinPool.commonPool-worker-3
Map: 3 - ForkJoinPool.commonPool-worker-4
Map: 6 - main
ForEach: 6 - main
ForEach: 1 - main
ForEach: 2 - ForkJoinPool.commonPool-worker-1
ForEach: 3 - ForkJoinPool.commonPool-worker-4
ForEach: 4 - ForkJoinPool.commonPool-worker-3
ForEach: 5 - ForkJoinPool.commonPool-worker-2

- 배열 numbers를 데이터 소스로 사용하여 parallel stream을 생성하고, map 연산과 forEach 연산을 순차적으로 수행.

- map 연산은 각 요소를 변환하고, forEach 연산은 각 요소를 출력.

- Thread.currentThread().getName() : 현재 실행 중인 스레드의 이름 출력.

- map 연산은 여러 스레드에서 동시에 실행되므로 요소의 처리 순서가 보장되지 않음. 

- forEach 연산은 마지막에 순차적으로 수행되며, 최종 결과인 출력도 순차적으로 출력

※ 중간 연산과 최종 연산의 특성에 따라 다를 수 있으므로, 순서에 의존하는 작업을 수행하는 경우에는 주의가 필요

 

Java 8 에 추가된 기능인 Stream 에 대해서 간략하게 알아보았습니다. 더불어 parallel stream 과의 비교도 확인하였습니다.

반응형
반응형

이번 글에서는 주사위 앱 만들기에 대해서 알아봅니다.

 

주사위 앱 레이아웃

 - BottomNavigationBar 위젯을 이용해서 화면 전환을 한다.

 - RootScreen 위젯 : 상단과 하단으로 나누어져 있는 위젯입니다. 하단에는 화면 전환을 할 수 있는 BottomNavigationBar 위젯을 위치 시키고, 상단에는 TabBarView 를 통해 선택된 화면을 보여줍니다. 화면 전환은 BottomNavigationBar 의 탭을 클릭하거나 TabBarView 에서 좌우 스크롤을 통해 이루어집니다. TabBarView 에 나타내는 화면은 HomeScreen 위젯과 SettingsScreen 위젯 두 화면입니다.

 - HomeScreen 위젯 : 주사위 이미지를 위치시킬 Image 위젯, 글자를 작성할 Text 위젯, 주사위 숫자를 나타낼 Text 위젯으로 이루어져 있습니다.

 - SettingsScreen : 민감도를 정의하는 Slider 위젯을 위치시키고 이 Slider 를 좌우로 이동시켜 민감도를 정합니다. Text 위젯을 이용하여 레이블을 작성합니다.

 

주사위 앱 shake 플러그인 및 이미지 추가

 - pubspec.yaml

....

dependencies:
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.2
  shake: 2.1.0

dev_dependencies:
  flutter_test:
    sdk: flutter

  # The "flutter_lints" package below contains a set of recommended lints to
  # encourage good coding practices. The lint set provided by the package is
  # activated in the `analysis_options.yaml` file located at the root of your
  # package. See that file for information about deactivating specific lint
  # rules and activating additional ones.
  flutter_lints: ^2.0.0

# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

# The following section is specific to Flutter packages.
flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  assets:
    - asset/img/
  
  ....

 

주사위 앱에서 사용할 상수 정의

 - 글자 크기, 자주 사용하는 색상등을 반복적으로 사용하는 값들을 미리 정의해놓고 사용하기 위해서 정의한다.

 - 일괄적으로 변경 시 유용하게 사용됩니다.

 - [프로젝트 디렉토리]/lib/const/colors.dart

import 'package:flutter/material.dart';

const backgroundColor = Color(0xFF0E0E0E);

const primaryColor = Colors.white;

final secondaryColor = Colors.grey[600];

※ secondaryColor : grey[600] 은 런타임시에 색상이 계산되기 때문에 const 사용이 불가하다. 그래서 final 로 정의합니다.

 

 

주사위 앱 위젯 구현

1. HomeScreen

 - [프로젝트 디렉토리]/lib/screen/home_screen.dart

import 'package:[프로젝트 명]/const/colors.dart';
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  final int number;

  const HomeScreen({
    required this.number,
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [

        // ➊ 주사위 이미지
        Center(
          child: Image.asset('asset/img/$number.png'),
        ),
        SizedBox(height: 32.0),
        Text(
          '주사위 숫자',
          style: TextStyle(
            color: secondaryColor,
            fontSize: 20.0,
            fontWeight: FontWeight.w700,
          ),
        ),
        SizedBox(height: 12.0),
        Text(
          number.toString(),  // ➋ 주사위 값에 해당되는 숫자
          style: TextStyle(
            color: primaryColor,
            fontSize: 60.0,
            fontWeight: FontWeight.w200,
          ),
        ),
      ],
    );
  }
}

 - Column 위젯을 사용하여 Image 위젯과 Text 위젯을 배치합니다. 화면에 보여질 숫자를 RootScreen 위젯에서 정의하도록 생성자를 통해서 number 매개변수로 입력받는다.

 - 생성자로 입력받은 number 매개변수 값을 이용하여 Image 위젯에는 해당하는 숫자 이미지를, Text 위젯에는 숫자 값을 표시합니다.

 

2. SettingsScreen

 - [프로젝트 디렉토리]/lib/screen/settings_screen.dart

import 'package:[프로젝트 명]/const/colors.dart';
import 'package:flutter/material.dart';

class SettingsScreen extends StatelessWidget {
  final double threshold;  // Slider의 현잿값

  // Slider가 변경될 때마다 실행되는 함수
  final ValueChanged<double> onThresholdChange;

  const SettingsScreen({
    Key? key,

    // threshold와 onThresholdChange는 SettingsScreen에서 입력
    required this.threshold,
    required this.onThresholdChange,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Padding(
          padding: const EdgeInsets.only(left: 20.0),
          child: Row(
            children: [
              Text(
                '민감도',
                style: TextStyle(
                  color: secondaryColor,
                  fontSize: 20.0,
                  fontWeight: FontWeight.w700,
                ),
              ),
            ],
          ),
        ),
        Slider(
          min: 0.1,  // ➊ 최소값
          max: 10.0,  // ➋ 최대값
          divisions: 101,  // ➌ 최소값과 최대값 사이 구간 개수
          value: threshold,  // ➍ 슬라이더 선택값
          onChanged: onThresholdChange,  // ➎ 값 변경 시 실행되는 함수
          label: threshold.toStringAsFixed(1),  // ➏ 표시값
        ),
      ],
    );
  }
}

 - SettinsScreen 위젯은 Text 위젯과 Slider 위젯으로 이루어져 있습니다. Column 위젯을 이용하여 Text 위젯과 Slider 위젯을 세로로 배치합니다.

 - Slider 위젯 : 좌우로 움직일 때 onChanged 매개변수에 정의된 콜백함수가 호출됩니다. onChanged 매개변수로 입력받은 현재값을 State에 저장하고 다시 value 매개변수에 같은 값을 입력하게 됩니다.

 - Slider 위젯은 현재값과 onChanged 매개변수는 RootScreen 에서 입력받습니다.

 

3. RootScreen

 - [프로젝트 디렉토리]/lib/screen/root_screen.dart

import 'package:flutter/material.dart';
import 'package:[프로젝트 명]/screen/home_screen.dart';
import 'package:[프로젝트 명]/screen/settings_screen.dart';
import 'dart:math';
import 'package:shake/shake.dart';

class RootScreen extends StatefulWidget {
  const RootScreen({Key? key}) : super(key: key);

  @override
  State<RootScreen> createState() => _RootScreenState();
}

class _RootScreenState extends State<RootScreen> with TickerProviderStateMixin{    // ➊
  TabController? controller;  // 사용할 TabController 선언
  double threshold = 2.7;
  int number = 1;
  ShakeDetector? shakeDetector;

  @override
  void initState() {
    super.initState();

    controller = TabController(length: 2, vsync: this);  // ➋

    controller!.addListener(tabListener);
    shakeDetector = ShakeDetector.autoStart(   // ➊ 흔들기 감지 즉시 시작
      shakeSlopTimeMS: 100,  // ➋ 감지 주기
      shakeThresholdGravity: threshold,  // ➌ 감지 민감도
      onPhoneShake: onPhoneShake,  // ➍ 감지 후 실행할 함수
    );
  }

  void onPhoneShake() {  // ➎ 감지 후 실행할 함수
    final rand = new Random();

    setState(() {
      number = rand.nextInt(5) + 1;
    });
  }

  tabListener() {  // ➋ listener로 사용할 함수
    setState(() {});
  }

  @override
  dispose(){
    controller!.removeListener(tabListener); // ➌ listener에 등록한 함수 등록 취소
    shakeDetector!.stopListening();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: TabBarView(  // ➊ 탭 화면을 보여줄 위젯
        controller: controller,
        children: renderChildren(),
      ),

      // ➋ 아래 탭 네비게이션을 구현하는 매개변수
      bottomNavigationBar: renderBottomNavigation(),
    );
  }

  List<Widget> renderChildren(){
    return [
      HomeScreen(number: number),
      SettingsScreen(  // 기존에 있던 Container 코드를 통째로 교체
        threshold: threshold,
        onThresholdChange: onThresholdChange,
      ),
    ];
  }

  void onThresholdChange(double val){  // ➊ 슬라이더값 변경 시 실행 함수
    setState(() {
      threshold = val;
    });
  }

  BottomNavigationBar renderBottomNavigation() {
    return BottomNavigationBar(
      currentIndex: controller!.index,
      onTap: (int index) {  // ➎ 탭이 선택될 때마다 실행되는 함수
        setState(() {
          controller!.animateTo(index);
        });
      },
      items: [
        BottomNavigationBarItem(  // ➊ 하단 탭바의 각 버튼을 구현
          icon: Icon(
            Icons.edgesensor_high_outlined,
          ),
          label: '주사위',
        ),
        BottomNavigationBarItem(
          icon: Icon(
            Icons.settings,
          ),
          label: '설정',
        ),
      ],
    );
  }
}

 - TabBarView 는 TabController 가 필수입니다. TabController 는 위젯이 생성될 때 딱 한번만 초기화되어야 하니 initState() 에서 초기화 작업을 합니다. TabController vsync 기능을 사용하려면 필수로 TickerProviderStateMixin을 사용해야 합니다. TickerProviderStateMixin 은 애니메이션의 효율을 올려주는 역할을 합니다. TabController 의 length 매개변수에는 탭의 개수를 나타내고, TickerProviderStateMixin 를 사용하는 State 클래스를 this 로 넣어줍니다. 그러면 controller 를 이용해서 TabBarView 를 조작할 수 있습니다. controller 의 속성이 변할 때마다 실행시킬 콜백함수를 addListener() 함수를 통해 등록할 수 있습니다. setState() 를 실행하여 controller 의 속성이 변경될 때 build() 를 재실행하도록 합니다.

 - ShakeDetector : Shake 플러그인은 흔들기를 인지할 때마다 실행할 함수를 등록합니다. 얼마나 자주 흔들기 감지 주기, 감지 민감도, 감지 시 실행할 콜백함수 등을 매개변수로 전달합니다. 흔들 때마다 1 ~ 6 사이의 난수를 생성합니다.

 - renderChildren() : TabBarView 위젯을 반환하는 함수입니다. TabBarView 위젯을 이용하면 각종 Tab 위젯과 쉽게 연동 할 수 있는 UI 를 구현할 수 있습니다. 기본 애니메이션이 제공되며 children 매개변수에 각 탭의 화면에서 활용하고 싶은 위젯을 List 로 넣어줍니다. bottomNavigation 매개변수에 BottomNavigationBar 를 넣어주면 쉽게 Tab을 조정할 수 있는 UI 를 하단에 배치할 수 있습니다. 주사위 앱에서는 TabBarView 의 첫번째 화면에 HomeScreen, 두번째 화면에 SettinsScreen 을 표시합니다.

 - renderBottomNavigation() : BottomNavigationBar 위젯을 반환하는 함수입니다. BottomNavigationBar 에 제공될 각 탭은 BottomNavigationBar 위젯의 items 매개변수에 제공해주면 됩니다. BottomNavigationBarItem 의 icon 매개변수와 label 매개변수를 이용해서 구현합니다. 탭을 클릭했을 때 TabBarView 와 화면을 동기화하기 위해 animateTo() 를 실행하여 자연스러운 애니메이션으로 TabBarView 를 전환시킵니다.

 

4. 주사위 앱 실행 첫 화면 : main

 - [프로젝트 디렉토리]/lib/main.dart

import 'package:flutter/material.dart';
import 'package:[프로젝트 명]/screen/home_screen.dart';
import 'package:[프로젝트 명]/const/colors.dart';
import 'package:[프로젝트 명]/screen/root_screen.dart';

void main() {
  runApp(
    MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        scaffoldBackgroundColor: backgroundColor,
        sliderTheme: SliderThemeData(  // Slider 위젯 관련
          thumbColor: primaryColor,    // 동그라미 색
          activeTrackColor: primaryColor,  // 이동한 트랙 색

          // 아직 이동하지 않은 트랙 색
          inactiveTrackColor: primaryColor.withOpacity(0.3),         ),
        // BottomNavigationBar 위젯 관련
        bottomNavigationBarTheme: BottomNavigationBarThemeData(
          selectedItemColor: primaryColor,     // 선택 상태 색
          unselectedItemColor: secondaryColor, // 비선택 상태 색
          backgroundColor: backgroundColor,    // 배경 색
        ),
      ),
      home: RootScreen(),
    ),
  );
}

 - Theme 설정 : 상수를 사용해서 테마를 적용합니다.

 - 홈 화면 : RootScreen 위젯으로 설정합니다.

 

 

주사위 앱을 실제 기기에서 실행해봅니다.

 - 주사위가 보이면 핸드폰을 흔들어서 난수가 생성되고 새로운 숫자와 주사위가 보이는 것을 확인합니다.

 - TabBarView 에서 좌우 스크롤하거나 BottomNavigationBar 를 클릭하여 화면 전환이 되는지 확인합니다.

 - [설정] 탭을 클릭하여 설정화면으로 이동한 뒤 Slider 위젯을 이용하여 민감도를 올립니다.

 - [주사위] 탭을 클릭하여 HomeScreen 화면으로 이동한 뒤 핸드폰을 더 강하게 흔들어야 난수가 발생하는지 확인합니다.

 

이상 플러터에서 주사위 앱을 만들고 실행까지 해보았습니다.

 

반응형
반응형

Java Stream reduce 함수에 대한 설명입니다.

 

바로 소스로 들어가보자!

import java.util.OptionalInt;
import java.util.stream.IntStream;

public class StreamReduce {

    public static void main(String[] args) {
        //Optional<T> reduce(BinaryOperator<T> accumulator);
        OptionalInt reduced1 = IntStream.range(1, 5) // [1, 2, 3, 4]
                        .reduce((a, b) -> {
                            System.out.println("a = " + a);
                            System.out.println("b = " + b);
                            return Integer.sum(a, b);
                        });

        System.out.println(reduced1.getAsInt());


        //T reduce(T identity, BinaryOperator<T> accumulator);
        int reduced2 = IntStream.range(1, 5) // [1, 2, 3, 4]
                        .reduce(10, (a, b) -> {
                            System.out.println("a = " + a);
                            System.out.println("b = " + b);
                            return Integer.sum(a, b);
                        });

        System.out.println(reduced2);
    }
}

 

긴말 필요 없이 결과도 바로 확인해보자!

a = 1
b = 2
a = 3
b = 3
a = 6
b = 4
10

a = 10
b = 1
a = 11
b = 2
a = 13
b = 3
a = 16
b = 4
20

 

Stream 생성 했던 IntStream 의 range 함수는 Javadoc 으로 확인해본다.

IntStream (Java Platform SE 8 ) (oracle.com)

 

IntStream (Java Platform SE 8 )

Returns an infinite sequential ordered IntStream produced by iterative application of a function f to an initial element seed, producing a Stream consisting of seed, f(seed), f(f(seed)), etc. The first element (position 0) in the IntStream will be the prov

docs.oracle.com

 

※ 첫번째 파라미터는 포함, 두번째 파라미터는 불포함

 - 항상 헷갈리는 부분이니까 꼼꼼히 읽어보자!!

static IntStream range(int startInclusive, int endExclusive)
Returns a sequential ordered IntStream from startInclusive (inclusive) to endExclusive (exclusive) by an incremental step of 1.
API Note:
An equivalent sequence of increasing values can be produced sequentially using a for loop as follows:

     for (int i = startInclusive; i < endExclusive ; i++) { ... }
 
Parameters:
startInclusive - the (inclusive) initial value
endExclusive - the exclusive upper bound
Returns:
a sequential IntStream for the range of int elements

 

 

자, 이제 본격적으로 Stream reduce 에 대한 설명 들어보고 가실께여~

1. 먼저 파라미터가 1개인 reduce 함수

        //Optional<T> reduce(BinaryOperator<T> accumulator);
        OptionalInt reduced1 = IntStream.range(1, 5) // [1, 2, 3, 4]
                        .reduce((a, b) -> {
                            System.out.println("a = " + a);
                            System.out.println("b = " + b);
                            return Integer.sum(a, b);
                        });

        System.out.println(reduced1.getAsInt());

첫번째 실행

 - a : Stream 의 첫번째 값 = 1

 - b : Stream 의 두번째 값 = 2

 - a + b = 3 을 반환(return) 한다.

두번째 실행

 - a : 첫번째 실행한 결과 값 = 3

 - b : Stream 의 다음(세번째) 값 = 3

 - a + b = 6 을 반환(return) 한다.

세번째 실행

 - a : 두번째 실행한 결과 값 = 6

 - b : Stream 의 다음(네번째) 값 = 4

 - a + b = 10 을 반환(return) 한다.

최종 실행 결과 값으로 10을 반환한다.

 

2. 먼저 파라미터가 2개인 reduce 함수

 - reduce 함수의 첫번째 파라미터는 초기 값을 의미한다.

        //T reduce(T identity, BinaryOperator<T> accumulator);
        int reduced2 = IntStream.range(1, 5) // [1, 2, 3, 4]
                        .reduce(10, (a, b) -> {
                            System.out.println("a = " + a);
                            System.out.println("b = " + b);
                            return Integer.sum(a, b);
                        });

        System.out.println(reduced2);

 

첫번째 실행

 - a : 초기 값 = 10

 - b : Stream 의 첫번째 값 = 1

 - a + b = 11 을 반환(return) 한다.

두번째 실행

 - a : 첫번째 실행한 결과 값 = 11

 - b : Stream 의 다음(두번째) 값 = 2

 - a + b = 13 을 반환(return) 한다.

세번째 실행

 - a : 두번째 실행한 결과 값 = 13

 - b : Stream 의 다음(세번째) 값 = 3

 - a + b = 16 을 반환(return) 한다.

네번째 실행

 - a : 세번째 실행한 결과 값 = 16

 - b : Stream 의 다음(네번째) 값 = 4

 - a + b = 20 을 반환(return) 한다.

최종 실행 결과 값으로 20을 반환한다.

 

 

위에서 살펴본 Stream reduce 두 함수의 차이는 초기 값이 있냐 없냐의 차이이다.

그리고 Stream reduce 함수에서 가장 중요하게 봐야할 부분은 accumulator 의 동작 방식이다.

첫번째, 두번째 인자에 값이 어떻게 전달되어 최종 결과값을 반환하는지에 대한 이해만 있다면 어렵지 않을 것이다.

 

Stream reduce 에 대한 Javadoc 페이지도 같이 확인하면 좋을 것 같아 링크를 건다.

Stream (Java Platform SE 8 ) (oracle.com)

 

Stream (Java Platform SE 8 )

A sequence of elements supporting sequential and parallel aggregate operations. The following example illustrates an aggregate operation using Stream and IntStream: int sum = widgets.stream() .filter(w -> w.getColor() == RED) .mapToInt(w -> w.getWeight())

docs.oracle.com

 

반응형

+ Recent posts