1. Java 전공책의 스레드 관련 내용을 복습했다.
    네트워크 프로그래밍도 복습했는데 아직 정리를 마치지 못했다.

  2. 이분 탐색 알고리즘 문제를 3개 풀이했다.

[오늘의 학습 키워드]

#스레드 #이분 탐색


[학습 내용 정리]

  • 스레드

https://devhippo.tistory.com/45

 

Java - 스레드(Thread)

[ Thread ]우리는 컴퓨터에서 여러 개의 작업을 동시에 진행하는 것이 가능하다. 한 개의 CPU가 작업을 잘게 쪼개어 모든 작업을 차례로 짧은 시간 동안 실행한다.CPU가 여러 개이거나 하나의 CPU에

devhippo.tistory.com

 

  • 문제 풀이

https://devhippo.tistory.com/46

 

1920번 - 이분 탐색 - 수 찾기

[ 풀이 ]정수의 범위가 너무 커서 배열을 통해서 이전에 찾은 값을 저장해 두는 것은 안 될 것 같았다.대신 탐색 경우를 조금이라도 줄이기 위해서 2가지를 조건문으로 확인했다.1. 찾고자 하는

devhippo.tistory.com

https://devhippo.tistory.com/47

 

10816번 - 이분 탐색 - 숫자 카드 2

[ 풀이 ]lower_bound와 upper_bound를 이용했다.(탐색된 값이 초과되는 지점 - 탐색값이 시작되는 지점)으로 중복된 개수를 구하였다.[ 코드 ]#include #include #include using namespace std;int n = 500000;int m = 500000;vec

devhippo.tistory.com

https://devhippo.tistory.com/48

 

1654번 - 이분 탐색 - 랜선 자르기

[ 풀이 ]서로 다른 길이의 N개의 랜선을 잘랐을 때, 동일한 길이의 랜선이 K개 이상이어야 한다.-> 동일한 길이의 랜선 개수 = x cm로 각 랜선을 나눴을 때의 몫의 합 최대 랜선의 길이를 구하자-> K

devhippo.tistory.com

 


[학습하며 겪었던 문제점]

1920번 문제에서 출력 초과 오류가 발생했다.
이를 해결하는데 시간을 조금 소비했다.

원인은 탐색되지 않았을 때의 경우를 처리해주지 않아서였다.

원인을 파악한 후에는 쉽게 문제를 해결했다.


[ 내일 학습 할 것 ]

1. 네트워크 프로그래밍 정리 완료하기

[ 풀이 ]

서로 다른 길이의 N개의 랜선을 잘랐을 때, 동일한 길이의 랜선이 K개 이상이어야 한다.
-> 동일한 길이의 랜선 개수 = x cm로 각 랜선을 나눴을 때의 몫의 합

 최대 랜선의 길이를 구하자
-> K를 만족하는 길이를 찾아도 우측 탐색을 지속함

랜선의 길이는 2^31-1보다 작거나 같은 자연수이다.
-> 정수가 매우 큰 단위까지 사용된다.
-> long long 타입 사용

[ 코드 ]

#include <stdio.h>
#include <algorithm>
#include <vector>

using namespace std;

typedef long long ll;

int n = 10000;

vector<ll> lens;

int calcNumofLen(int length) { //동일한 길이의 랜선 개수 계산
	ll result = 0;
	for (auto l : lens) {
		result += l / length;
	}
	return result;
}

int main() {
	ll k;
	scanf("%d %d", &n, &k);

	ll length = 0;
	ll max_length = 0;
	for (int i = 0; i < n; i++) {
		scanf("%d", &length);
		lens.push_back(length);
		if (length > max_length) {
			max_length = length;
		}
	}

	ll left = 1;
	ll right = max_length;
	ll result = max_length;

	while (left <= right) {
		ll mid = (left + right) / 2;
		ll count = calcNumofLen(mid);
		if (count < k) {
			right = mid - 1;
		}
		else { //조건 만족 시
			result = mid; //길이 저장
			left = mid + 1; //우측 탐색 지속
		}
	}

	printf("%d\n", result);

	return 0;
}

[ 풀이 ]

lower_bound와 upper_bound를 이용했다.
(탐색된 값이 초과되는 지점 - 탐색값이 시작되는 지점)으로 중복된 개수를 구하였다.

[ 코드 ]

#include <stdio.h>
#include <algorithm>
#include <vector>

using namespace std;

int n = 500000;
int m = 500000;
vector<int> cards;

int checkCardsNum(int num) { //중복되는 카드 개수 세기
	auto minAdd = lower_bound(cards.begin(), cards.end(), num);
	auto maxAdd = upper_bound(cards.begin(), cards.end(), num);
	int result = maxAdd - minAdd;
	return result;
}

int main() {
	scanf("%d", &n);

	int card = 0;
	for (int i = 0; i < n; i++) {
		scanf("%d", &card);
		cards.push_back(card);
	}
	
	sort(cards.begin(), cards.end());

	int minNum = cards.front();
	int maxNum = cards.back();
	
	scanf("%d", &m);
	int target = 0;
	for (int i = 0; i < m; i++) {
		scanf("%d", &target);
		if (target > minNum && target < maxNum) {
			int left = 0;
			int right = m - 1;
			bool isExist = false;
			while (left <= right) {
				int mid = (left + right) / 2;
				if (cards[mid] == target) { //범위 내에서 값이 존재하는 경우
					printf("%d\n", checkCardsNum(target));
					isExist = true;
					break;
				}
				else if (cards[mid] < target) {
					left = mid + 1;
				}
				else {
					right = mid - 1;
				}
			}

			if (!isExist) {
				printf("0\n");
			}
		}

		else if (target == minNum || target == maxNum) {
			printf("%d\n", checkCardsNum(target));
		}

		else {
			printf("0\n");
		}
	}

	return 0;
}

[ 풀이 ]

정수의 범위가 너무 커서 배열을 통해서 이전에 찾은 값을 저장해 두는 것은 안 될 것 같았다.
대신 탐색 경우를 조금이라도 줄이기 위해서 2가지를 조건문으로 확인했다.

1. 찾고자 하는 값이 저장된 정수 범위 내에 있는가.
2. 정수 범위의 최솟값 또는 최댓값과 동일한가.

찾고자 하는 값이 저장된 정수 범위 내에 있는 경우 이분 탐색을 진행한다.
값이 존재하면 1을 출력하고, 존재하지 않으면 bool 변수를 통해 판별하여 0을 출력하도록 했다.
(이분탐색을 별도의 함수로 정의해서 반환값으로 판별해도 좋을 것 같다.)

[ 어려웠던 점 ]

출력 초과 오류가 발생해서 이를 해결하는 데 시간이 조금 걸렸다.
탐색이 되지 않았을 때를 고려하지 않아서 발생한 문제였다.
isExist 변수로 이를 처리하니 오류를 해결할 수 있었다.

[ 코드 ]

#include <stdio.h>
#include <algorithm>
#include <vector>

using namespace std;

int n = 100000;
int m = 100000;

int main(void) {

	scanf("%d", &n);

	vector<int> nums(n);
	for (int i = 0; i < n; i++) {
		scanf("%d", &nums[i]);
	}

	sort(nums.begin(), nums.end());

	int minNum = nums.front();
	int maxNum = nums.back();

	scanf("%d", &m);

	int target = 0;
	for (int i = 0; i < m; i++) {
		scanf("%d", &target);
        //범위 내에 있는 값인 경우
		if (target >= minNum && target < maxNum) {
			int left = 0;
			int right = nums.size() - 1;
			bool isExist = false;
			while (left <= right) {
				int mid = (left + right) / 2;
				if (nums[mid] == target) {
					printf("1\n");
					isExist = true;
					break;
				}
				else if (nums[mid] < target) {
					left = mid + 1;
				}
				else {
					right = mid - 1;
				}
			}

			if (!isExist) { //일치하는 값이 없는 경우
				printf("0\n");
			}
		}
		
		else { //최소값 또는 최댓값과 동일한 경우
			if (target == minNum || target == maxNum) {
				printf("1\n");
			}
			else {
				printf("0\n");
			}
		}
	}

	return 0;
}

[ Thread ]

우리는 컴퓨터에서 여러 개의 작업을 동시에 진행하는 것이 가능하다. 

한 개의 CPU가 작업을 잘게 쪼개어 모든 작업을 차례로 짧은 시간 동안 실행한다.
CPU가 여러 개이거나 하나의 CPU에 여러 개의 코어가 있다면,
각각의 CPU 또는 코어가 하나씩 작업을 진행할 수 있으므로 같은 시간대에 여러 개의 작업이 병렬적으로 실행될 수 있다.

이때, 잘게 쪼개어지는 작업을 스레드(Thread)라고 한다.
하나의 프로세스 내에 여러 개의 쓰레드가 존재할 수 있고, 스레드들이 동시에 실행될 수 있다.
이를 멀티쓰레딩이라고 한다.

< Thread는 직원, GPU는 대기업... >

나는 GPU기반 CUDA를 먼저 학습하고 사용했기 때문에 병렬프로그래밍에 익숙한 편이다.
병렬프로그래밍을 공부하는 초기에  thread를 직원에 비유해서 이해했다.

더보기
더보기

thread를 직원이라고 할 때,
1인 회사는 thread가 하나밖에 없다.
그러니까 회사 프로젝트 진행, 회계 관리 등의 업무를 혼자 다 해야 한다.
스케줄에 지장이 가지 않을 정도로 분량을 쪼개고 순서를 정해서 혼자 처리하는 것이다.

멀티코어나 여러 개의 CPU는 기업이다.
직원이 어느 정도 있다.
1인 회사가 혼자 해야 하던 일을 여러 명이 나눠서 하는 것이다. (멀티스레딩)
당연히 많은 양을 더 빨리 처리할 수 있다.

그런 의미에서 GPU는 대기업이라고 할 수 있겠다. 
(비싼 가격에는 그만한 이유가 있는...)

< Thread 작성 방법 >

자바에서 Thread를 생성하는 방법은 2가지이다.

  1. Thread 클래스를 상속받는 방법
  2. Runnable 인터페이스를 구현하는 방법

1번은 간단하고 2번은 유용하다.

1. Thread 클래스 상속

  • Thread의 서브 클래스 정의
  • run 메서드 오버라이드 (Thread가 실행할 작업 내용 입력)
  • Thread를 상속받으면 다른 클래스를 상속할 수 없게 된다. (당연한 이야기, 단일 상속이니까)
public class MyThread extends Thread { 
	private Strng threadName;
    
    public MyThread(String name){
    	threadName = name;
    }
    public void run(){
    	for(int i = 0; i < 10; i++){
        	try{
            	Thread.sleep(10);		//Thread를 10ms 대기시킴
            }
            catch(InterruptedException e){	//sleep으로 인해 발생할 수 있는 예외
            	e.printStackTrace();
            }
        }
    }
}

Thread t1 = new MyThread("thread1"); //Thread 객체 생성
t1.start(); //thread를 실행시킨다.

반드시 start를 통해서 스레드를 시작시켜야 한다.
run을 통해 실행시키면 병렬프로그래밍이 되지 않는다.

2. Runnable 인터페이스 구현

  • Runnable 인터페이스를 상속한다.
  • 클래스 내에서 run 메서드를 구현한다.
public class MyThread implements Runnable{ //Runnable 인터페이스 상속
	private String threadName;
   	
    public MyThread(String name){
    	threadName = name;
    }
    
    public void run(){ //run 메소드 재정의
    	//Thread가 할 일
    }
}

Thread t1 = new Thread(new MyThread("thread1"));
t1.start();
Thread 메소드 설명
run() 스레드가 실행할 작업을 정의. 반환 값이 없다.
start() Runnable 객체를 새 스레드로 실행
join() 해당 메소드를 호출한 스레드 종료까지 메인 스레드가 대기
sleep(ms) 스레드를 일정 시간 (밀리초) 동안 일시 정지

 

< Thread의 동기화 (Synchronization) >

동기화는 한 번에 하나의 스레드만이 임계 영역을 실행하도록 제어하는 방법이다.
하나의 스레드가 공유 자원에 대한 조작을 끝낸 다음, 다른 쓰레드가 해당 자원에 접근할 수 있게 된다.

* 임계 (코드) 영역 (Critical code section)

쓰레드 간의 실행 순서에 따라 결과가 달라질 수 있는 영역
공유 자원을 조작하는 코드 부분

 

동시에 실행되는 여러 개의 쓰레드가 동일한 공유 자원을 접근할 때, 문제가 생길 수 있다.

> 문제가 생기는 예시

더보기
더보기

예를 들어, 잔액이 10000원 일 때, Thread1이 5000원을 입금하고 Thread2가 3000원을 출금한다고 하자.
우리가 원하는 건, Thread1에서 저장된 결과를 Thread가 사용하는 것이다.

Thread1 : 10000 + 5000 = 15000
Thread2 : 10000 - 3000 = 7000

그러나 동시에 같은 자원에 접근했기 때문에 둘 다 10000원을 기준으로 연산을 하고,
Thread1이 15000원을 저장했지만 이후 Thread2가 7000원을 저장했기 때문에 잔액이 7000원으로 출력되게 된다.


이런 문제를 해결하는 방법이 바로 동기화이다. 
메서드 전체 또는 일부 코드에 대해서 동기화할 수 있다.

public synchronized void test(){}//메소드 전체를 동기화

public void test2(){
	synchronized(sync_object){ //일부 코드만 동기화
    	//공유 자원에 접근하는 코드
    }
    //그 외의 코드
}

 

> 동기화 객체 ( syncObject )


일부 코드에 대해서 동기화 할 때는 sync_object가 필요하다.
sync_object는 동기화 객체 또는 락 객체라고 한다.

  • 동기화를 위한 기준점 역할을 한다.
  • 한 시점에 한 개의 스레드만 이 객체에 대한 락을 획득할 수 있다.

  • 객체의 타입에 제한이 없다. (기본타입 int, double 등 제외)
  • null 절대 불가
  • 중간에 객체 타입이 변해선 안된다.
  • 동일한 리소스에 대해서 동일한 동기화 객체를 사용해야 한다.
    (서로 다른 동기화 객체 사용 시, 동기화 효과가 사라진다.)

동기화 객체의 타입에 제한이 없기 때문에 다양한 방법으로 객체를 사용할 수 있다.

  1. 전용 동기화 객체 
  2. this : 현재 인스턴스 전체에 대한 동기화가 필요할 때 사용, 외부 간섭 주의
  3. 클래스 
  4. 필드 : 특정 필드에 대한 동기화가 필요할 때, 단 필드 변경 시 주의 필요
//전용 동기화 객체
private final Object lock = new Object();
//this
synchronized(this) { ... }
//클래스 객체
synchronized(MyClass.class) { ... }
//필드
private final List<String> list = new ArrayList<>();
synchronized(list) { ... }

 

> 멀티스레딩과 동기화 예제

더보기
더보기
public class Account implements Runnable { //Runnable 상속
    private int balance = 10000;

    public synchronized void deposit(int amount) { //임계 영역 동기화
        int cur_dep = balance;
        cur_dep += amount;
        balance = cur_dep;
    }

    public int getBalance() {
        return balance;
    }

    @Override
    public void run() {
        // 스레드 당 5번 입금 수행
        for (int i = 0; i < 5; i++) {
            deposit(1000);
            System.out.println(Thread.currentThread().getName() 
                + " 입금 후 잔액: " + getBalance());
        }
    }

    public static void main(String[] args) {
        Account account = new Account();

        // 두 스레드가 동일한 Account 객체 공유
        Thread t1 = new Thread(account, "thread1");
        Thread t2 = new Thread(account, "thread2");

        t1.start();
        t2.start();

        // 메인 스레드 대기
       try {
          t1.join(); //join는 InterruptedException 발생시킬 가능성이 있음
          t2.join();
       } catch (InterruptedException e) {
          e.printStackTrace();
       }


        System.out.println("최종 잔액: " + account.getBalance());
    }
}

 

 

< 생산자 - 소비자 관계 >

생산자-소비자 관계는 일정 크기의 저장소에 대해서, 
한 스레드는 데이터를 저장하고 다른 스레드는 저장된 데이터를 쓰는 관계를 말할 수 있다.
데이터를 저장하는 스레드는 용량이 다 차면 생산을 멈췄다가 빈 공간이 생기면 다시 생산을 시작한다.
저장된 데이터를 쓰는 스레드는 쓸 데이터가 없을 때는 소비를 멈췄다가 데이터가 생기면 다시 소비를 시작한다.

이런 상황을 처리하기 위해서 Object에서 제공하는 wait와 notify 메소드를 사용할 수 있다.

Object에서 제공하는 메소드 설명
wait() Thread가 일시 정지 상태에 들어가게 함
notify() Thread를 깨움
public synchronized void get(){
	while(isEmpty){
    	try{
        	wait(); //Thread를 일시 정지 시킴
        } catch(InterruptedException e){
        
        }
    }
    
    notify(); //Thread 깨우기
}

 

< 동기화 남발 주의! >

동기화를 남발하면 여러 개의 Thread를 사용해도 병렬프로그래밍이라고 볼 수 없게 된다.
(직원이 여러 명인데 줄 서서 한 명씩 일처리 하는 것과 동일하다.)
따라서, 임계 영역과 같이 필요한 최소의 영역만 동기화하는 것이 바람직하다.

 

'Java' 카테고리의 다른 글

JAVA의 구조  (0) 2025.02.24
Java - 네트워크 프로그래밍  (0) 2025.02.09
Java - 배열과 리스트  (1) 2025.02.06
Java - 인터페이스  (0) 2025.02.05
Java - 오버로딩, 오버라이딩  (1) 2025.02.04
  1. Java 전공책의 배열과 리스트 관련 내용을 복습했다.

  2. 가계부 프로젝트의 로그인 페이지를 완성했다.

[오늘의 학습 키워드]

#배열과 리스트


[학습 내용 정리]

  • 배열과 리스트

https://devhippo.tistory.com/43

 

Java - 배열과 리스트

[ ArrayList ]Java에서 사용할 수 있는 동적 배열이다.ArrayList에 요소를 추가할수록 ArrayList의 크기가 자동으로 늘어난다.ArrayList 변수명 = new ArrayList(초기 배열 크기);ArrayList 변수명 = new ArrayList(); //타

devhippo.tistory.com

 

  • 프로젝트 4일차

프로필 선택 시, 생성되는 비밀번호 입력창을 구현했다.
숫자만 입력 받기, 한 개씩 따로 입력 받기, 자동으로 포커스 바꾸기 등의 기능을 적용하고자 했다.

비밀번호 입력창
비밀번호 입력 시, 각 자리의 숫자를 입려 받는 input의 포커스

입력창에 포커스가 갈 경우 크기가 조금 커지도록 했다.
네모의 중심점이 같은 선상에 위치한 채, 크기가 커지길 원했는데
윗면이 같은 선상에 위치한 채, 크기가 커지는 결과로 만족해야했다.

4자리를 입력하지 않았을 때의 결과, 비밀번호가 일치하지 않을 때의 결과

비밀번호가 프로필 비밀번호와 일치하면 기존에 구현해 둔 가계부 입력창으로 넘어가고,
그 외의 경우 비밀번호 입력창이 리셋된다.

 


[학습하며 겪었던 문제점]

1. 화면 렌더링
새로고침을 하지 않고도 새로 추가된 프로필을 표시할 수 있는 방법이 있을까 고민하다가 
그냥 심플하게 프로필 추가에 성공 시, 화면을 새로고침하도록 했다.

 window.location.reload();

 

2. 4개의 비밀번호 input 요소 간의 간격 조절
justify-content: space-between(?) space-around(?)를 사용하니, 
4개의 input창이 서로 간격을 두고 떨어졌지만 그 간격이 너무 넓었다.
이를 변경하니 input창들이 딱 붙었다.

gap : px;

gap을 통해 원하는 간격을 설정할 수 있었다.

3. 숫자만 입력받기
JaveScript에서는 어떻게 숫자와 문자를 구분해야하나 고민했다.
검색해보니 문자열에서 특정 문자 조합을 찾기 위한 패턴인 정규 표현식이라는게 있었다.

// 숫자가 아닌 값이 입력되면 초기화
if (!/^\d$/.test(value)) {
  e.target.value = ""; // 숫자가 아니면 삭제
  return;
}

\d는 0-9의 숫자를 의미한다.
test 메소드를 이용해 입력값 value가 한 자리 숫자가 아닐 경우, 입력값을 삭제한다. 

전체적으로 JavaScript를 사용하는데 익숙하지 않고 모르는 요소도 너무 많았다. 
전공책에서 JavaScript 부분만이라도 한 번더 복습해야할 필요성을 느꼈다.   


[ 내일 학습 할 것 ]

1. Java 전공책 멀티스레드, 네트워크 내용 복습하기

2. 이분 탐색 코딩 문제 풀기(3개 정도)

[ ArrayList ]

Java에서 사용할 수 있는 동적 배열이다.
ArrayList에 요소를 추가할수록 ArrayList의 크기가 자동으로 늘어난다.

ArrayList<클래스명> 변수명 = new ArrayList<객체>(초기 배열 크기);

ArrayList<클래스명> 변수명 = new ArrayList<>(); //타입 추론, 디폴트 배열 크기 10 지정
  • ArrayList의 요소 타입으로 클래스만을 사용할 수 있다.
  • 초기 배열 크기를 따로 설정하지 않으면 디폴트로 10이 지정된다.
  • 배열의 요소 타입을 생략하면 컴파일러는 문맥으로 타입을 추론한다.
기본적인 메소드 설명
add(요소) 앞에서 부터 차례대로 비어있는 위치에 요소를 추가
add(index, 요소) 지정된 위치에 입력받은 요소를 삽입
해당 위치와 그 뒤의 요소들이 한 자리씩 뒤로 물러난다.
set(index, 요소) 지정된 위치의 요소를 입력받은 요소로 대체
remove(index) 지정된 위치의 요소를 삭제
뒤에 있던 요소들이 한 자리씩 앞으로 이동한다.
remove(요소) 주어진 요소와 동일한 첫 번째 요소를 삭제하고 true를 반환
clear() 리스트의 모든 요소를 삭제
isEmpty() 리스트가 텅 비어있으면 true 반환
contains(요소) 주어진 요소가 리스트에 존재하면 true를 반환
indexOf(요소) 주어진 요소가 리스트에 있으면, 첫 번째 나타난 요소의 인덱스를 반환
포함되어 있지 않으면 -1을 반환
get(위치) 지정된 위치의 요소를 반환
size() 현재 리스트의 크기를 반환

[ LinkedList ]

(이중)연결 리스트를 직접 구현하지 않고 사용할 수 있다.
삭제 연산이 많은 경우 ArrayList 대신 사용하면 좋다.

ArrayList 와 다르게 초기 크기를 인자로 받지 않는다.

LinkedList<클래스명> 변수명 = new LinkedList<>();

[ HashSet ]

집합을 구현하는 자료구조

  • 중복된 요소가 있을 수 없다.
  • 순서가 정의되지 않는다. (set, get 등 위치를 필요로 하는 함수 사용 불가)

[ HashMap ]

'키-값'의 쌍으로 구성된 컬렉션을 저장한다.

HashMap<키 타입, 값 타입> 변수명 = new HashMap<>();
메소드 설명
clear(), isEmpty() -
put(키, 값) 키-값 쌍으로 요소 추가
get(키) 키에 해당하는 값을 가져옴
keySet() 모든 키들의 집합을 만들어서 반환
values() 모든 값들의 컬렉션을  반환
remove(키) 키에 해당하는 키-값 쌍을 삭제
containsKey(키) 주어진 키가 존재하면 true를 반환
containsValue(값) 주어진 값이 존재하면 true를 반환

'Java' 카테고리의 다른 글

Java - 네트워크 프로그래밍  (0) 2025.02.09
Java - 스레드(Thread)  (0) 2025.02.07
Java - 인터페이스  (0) 2025.02.05
Java - 오버로딩, 오버라이딩  (1) 2025.02.04
Java - 상속  (1) 2025.02.03
  1. Java 전공책의 인터페이스 관련 내용을 복습했다.

  2. 가계부 프로젝트의 로그인 페이지 디자인을 완성했다.

  3. 밀린 집안일 해결과 로그인 페이지 완성에 집중하기 위해
    코딩 문제는 Solved.ac의 클래스 레벨을 올리기 위한 간단한 문제만 풀어서 따로 정리하지는 않았다.

[오늘의 학습 키워드]

#인터페이스

 


[학습 내용 정리]

  • 인터페이스

https://devhippo.tistory.com/41

 

Java - 인터페이스

[ 추상 클래스 ]메소드 정의 시, 몸체를 생략반환 타입 앞에 abstract 추가추상 메소드가 하나라도 포함되면 그 클래스는 추상 클래스가 된다.객체를 생성할 수 없다.public abstract class MyShape { //추상

devhippo.tistory.com

 

  • 프로젝트 3일차
    어제 구상한 디자인을 기반으로 화면을 다시 구상했다.
    프로필 선택 문구와 프로필 표시 영역을 묶어서 합쳐주었고
    프로필 창의 모양을 원형에서 모서리가 둥근 네모로 변경, +의 크기를 키웠다.

프로필 선택창

프로필을 추가하는 팝업창을 다음과 같이 구성했다.
입력창은 플로팅 라벨을 사용했고, 버튼은 커서가 영역 위에 올라가면
왼쪽 이미지에서 오른쪽 이미지와 같이 버튼 색상이 변경되도록 설정했다.

프로필 추가 팝업창 좌) 커서를 안 올렸을 때, 우) 커서를 버튼 위에 올렸을 때

새로 추가할 프로필의 정보를 입력하고 저장을 누르면 firebase의 서버에 데이터가 저장되고 프로필 창이 생성된다.
프로필 창을 누르면 비밀번호를 입력할 수 있다.

새로 추가된 프로필, 데이터 베이스에 저장된 모습, 프로필 선택 시 비밀번호 입력창


[학습하며 겪었던 문제점]

1. 플로팅 라벨의 문구가 보이지 않음
    CSS에서 body의 글자를 모두 흰색으로 설정했기 때문에
    흰 바탕 위에 흰색으로 placehold의 문구가 적혀서 보이지 않았던 것이다.
    해당 영역의 글자를 검정색으로 설정해줌으로써 해결했다.

2. JQuery 실행 안 됨
    CSS때와 동일한 이유로 JQuery 실행을 위한 링크를 삽입하지 않았기 때문에 발생한 문제였다.
    bootstrap때의 경험을 기반으로 빠르게 문제를 파악하고 해결할 수 있었다.

3. 렌더링
    새로고침을 눌러야만 새로 추가한 프로필 데이터가 업데이트 되었다.
    이 부분을 해결 중이다.     


[ 내일 학습 할 것 ]

1. Java 전공책 리스트, 멀티스레드 내용 복습하기

2. 프로젝트 화면 렌더링 문제 해결,
    비밀번호 입력창 디자인 바꾸기,
    올바른 비밀번호 입력 시 가계부 화면으로 이동하는 이벤트 작성하기

[ 추상 클래스 ]

  • 메소드 정의 시, 몸체를 생략
  • 반환 타입 앞에 abstract 추가
  • 추상 메소드가 하나라도 포함되면 그 클래스는 추상 클래스가 된다.
  • 객체를 생성할 수 없다.
public abstract class MyShape { //추상 메서드가 있기 때문에 추상 클래스이다
	public abstract void draw(Graphics g); //몸체 생략, 추상 메소드
}

추상 클래스에서 추상 메소드를 사용하는 이유는
추상 메소드가 서브클래스에서 오버라이딩 되도록 강제하기 위한 것이다.

추상 클래스를 상속받은 모든 서브클래스에 대해서 해당 추상 메소드를 오버라이딩하지 않아도
메소드 호출이 항상 올바르게 동작한다는 확신을 가질 수 있는 설계상의 이점 때문이다.

MyShape를 상속받은 서브클래스에서 draw를 호출하면
draw를 오버라이딩하지 않아도 호출은 정상적으로 동작한다.
단지, 오버라이딩하지 않았기 때문에 아무런 일도 일어나지 않을 뿐이다.

[ 인터페이스 ]

추상 메소드와 상수의 정의만을 포함하는 요소
업데이트 되면서 정적 메소드와 디폴트 메소드의 사용이 가능해졌다.

변수의 타입으로도 사용이 가능하다.

public intercface example{
	double getNum(); 					//묵시적으로 public으로 간주됨
	static int add(int a, int b){		//정적 메서드 사용가능
    	return a + b
    }
    default void result(){				//default 메서드 사용가능
    }
}

public class use implements example{	//implements 사용

}

< 다중 상속 >

  • 인터페이스 간에도 상속이 가능하다.
  • extends로 상속한다.
  • 인터페이스 간의 다중 상속이 가능하다.
  • 클래스 상속에서는 다중 상속이 불가능하지만, 인터페이스를 다중 상속 구현이 가능하다.
interface A { }
interface B { }
interface C extends A, B { } //인터페이스 간 다중 상속

class D implements A, B { } //클래스에 인터페이스 다중 상속

 

 

'Java' 카테고리의 다른 글

Java - 스레드(Thread)  (0) 2025.02.07
Java - 배열과 리스트  (1) 2025.02.06
Java - 오버로딩, 오버라이딩  (1) 2025.02.04
Java - 상속  (1) 2025.02.03
Java - 예외 처리  (0) 2025.01.31
  1. Java 전공책 복습을 이어갔다.
    오버라이딩 부분을 공부했다.

  2. 동적 탐색법 문제를 풀려고 했는데 이분 탐색을 알아야 했다.
    이분탐색을 공부하고 관련 문제를 하나 풀어본 후, 해당 문제 풀이를 완료했다.

  3. 가계부 프로젝트를 진행했다.
    정말 나만 쓰려고 했는데, 프로젝트 개발 이야기를 들은 친구가 자기도 쓰고 싶다고 얘기했다.
    보안 문제와 데이터 베이스 사용량 때문에 고민했는데
    한 명 정도는 괜찮을 것 같아서 흔쾌히 수락했다.
    친구에게만 배포 후, 필요한 기능을 추가해나가는 과정이 재밌을 것 같았다.
    따라서, 친구와 나를 구분해줄 로그인 페이지를 먼저 만들기로 했다.

[오늘의 학습 키워드]

#오버라이딩 #이분 탐색 #동적 계획법 #로그인 페이지

 


[학습 내용 정리]

  • 오버라이딩

https://devhippo.tistory.com/39

 

Java - 오버로딩, 오버라이딩

[ 오버로딩 overloading ]한 클래스 내에서 같은 이름의 메소드가 여러 개 정의될 때, 오버로딩 또는 메소드 중복이라고 한다.중복되는 메소드들은 매개변수의 개수 또는 타입이 달라야 한다.오버로

devhippo.tistory.com

 

  • 이분 탐색

https://devhippo.tistory.com/35

 

이분탐색 (Binary Search)

[ 이분탐색 (Binary Search) ]정렬된 데이터에서 특정 값을 찾는 알고리즘이다.탐색 범위를 반씩 줄여나간다.O(log n) 대규모 데이터에 대해서 효율적이다.#include #include #include using namespace std;int main() {

devhippo.tistory.com

 

  • 문제 풀이

https://devhippo.tistory.com/36

 

백준 2805번 - 이분 탐색 - 나무 자르기

https://www.acmicpc.net/problem/2805> 제한조건더보기시간제한 : 1초메모리 제한 : 256MB> 문제더보기상근이는 나무 M미터가 필요하다. 근처에 나무를 구입할 곳이 모두 망해버렸기 때문에, 정부에 벌목 허

devhippo.tistory.com

https://devhippo.tistory.com/37

 

백준 11053번 - 이분 탐색, 동적 계획법 - 가장 긴 증가하는 부분 수열

https://www.acmicpc.net/problem/11053> 제한조건더보기시간제한 : 1초메모리 제한 : 256MB> 문제더보기수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열을 구하는 프로그램을 작성하시오.예를 들어, 수열 A

devhippo.tistory.com

 

  • 프로젝트 2일차
    로그인 페이지 제작을 시작했다.
    어떻게 구성할지 감이 잘 잡히지 않다가 넷플릭스의 프로필 선택창을 모티브로 만들어야겠다고 생각했다.
    일단 브라우저에 유저 정보를 저장하는 방법으로 구성을 완료한 후,
    firebase 데이터 베이스에 연동되도록 변경할 계획이다. 

< 해야할 것 >

  1. 콘텐츠를 모두 화면의 정중앙으로 모으기
  2. 프로필 구성 변경하기 (이미지는 네모, 글자는 네모 아래로)
  3. firebase 연동하기
  4. 프로필 클릭 후, 비밀번호를 올바르게 입력 시 페이지 변경

 


[학습하며 겪었던 문제점]

  • 11053번 문제풀이
    2805번 문제보다 11053번 문제풀이를 먼저 도전했다.

    처음에는 단순하게 vector에 데이터를 정렬한 후, 중복을 제거하면 구하는 방법을 생각했다.
    순열은 순서가 중요한데, 이 경우 순열의 순서가 보장되지 않기 때문에 부적합하다고 결론지었다.

    또 다른 방법으로, 수열의 이전 위치의 최대값보다 큰 경우 길이 변수를 1 증가시키는 방법을 생각했다.
    그러나 이 방법의 경우 처음 입력되는 값이 큰 경우, 가장 긴 수열을 구할 수가 없었다.
    int n = 0;
    scanf("%d", &n);
    int a;
    int beforeNum = 0;
    int length = 0; 
    for (int i = 0; i < n; i++) {
        scanf("%d", &a);
        if (beforeNum < a && beforeNum != a) {
            beforeNum = a;
            length++;
            printf("%d\n", beforeNum);
        }
    }

    결국 풀이 방법을 인터넷으로 찾아보니 이분 탐색을 활용해서 풀어야한다고 했다.
    해당 문제 풀이를 잠시 뒤로 미뤄두고 이분 탐색을 먼저 공부한 후, 다시 문제를 푸니 훨씬 풀이가 쉬웠다.

    동적 계획법을 어떤 문제에서 사용해야할지 판별하는 부분이 아직도 어렵게 느껴졌다.
    동적 계획법 관련된 내용을 간단하게 정리해봤지만 부족한 부분이 많아서 앞으로 차근차근 업데이트할 계획이다.


[ 내일 학습 할 것 ]

1. Java 전공책 인터페이스 내용 복습하기

2. 이분 탐색 한 문제 복습하기, 분할 정복 문제 풀어보기

3. 프로필 선택창 개발 이어하기

+ Recent posts