<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>yeon_archive.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sun, 14 Jul 2024 12:26:43 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>yeon_archive.log</title>
            <url>https://velog.velcdn.com/images/yeon_archive/profile/75ed0ff3-e5c1-4a33-8cfd-926866be661e/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. yeon_archive.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/yeon_archive" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[혼공파] 2주차_chap3-4 ]]></title>
            <link>https://velog.io/@yeon_archive/%ED%98%BC%EA%B3%B5%ED%8C%8C-2%EC%A3%BC%EC%B0%A8</link>
            <guid>https://velog.io/@yeon_archive/%ED%98%BC%EA%B3%B5%ED%8C%8C-2%EC%A3%BC%EC%B0%A8</guid>
            <pubDate>Sun, 14 Jul 2024 12:26:43 GMT</pubDate>
            <description><![CDATA[<p>TMI) 계절학기로 영어, 글쓰기를 수강 중인데요
1학년 과목인데 안 들어서,, 업보 청산하고 있습니다 하하 (ಥ _ ಥ)
어제는 글쓰기 힘들어서 신논현 돌아다녔어요 
백금발 근처에 저뿐이었는데 가신 분들 중 절 보신 분이 있을지도 모르겠네용
쓰던 안경이 오래 쓰고 있으면 코 내려갈 거 같이 쫌 무거워서
하나 겟 했습니당
왼쪽은 진짜 얼굴의 절반을 가려서 오른쪽 안경으로 샀어요 😊
<img src="https://velog.velcdn.com/images/yeon_archive/post/d7ba7f3f-e571-47a3-9fc3-e1058b3a9a18/image.png" alt=""></p>
<p>사실 도수있는 안경 안 씀
블루라이트 차단입니당</p>
<p>암튼 과제 제출합니다</p>
<p>** 전체의 15%정도 이미지 올려도 되는 건 알고 있는데 
문제를 올려도 될까 싶어서 답&amp;이유만 적었습니다!</p>
<p>[기본과제]</p>
<h3 id="3-2-데이터-입력-p81-82">3-2 데이터 입력 (P.81-82)</h3>
<ol>
<li><p>scan 함수의 사용법이 옳은 것은 ? 3
이유는 int 타입에는 %d, float 타입에는 %f를 사용할 수 있기 때문입니다.</p>
</li>
<li><p>프로그램이 완성되도록 빈칸에 알맞은 내용 작성</p>
</li>
</ol>
<p>코드</p>
<pre><code>int main(void)
{
    char fruit[20];
    int cnt;

    printf(&quot;좋아하는 과일: &quot;);
    scanf(&quot;%s&quot;, fruit); // 1
    printf(&quot;몇 개: &quot;);
    scanf(&quot;%d&quot;, &amp;cnt); // 2
    printf(&quot;%s를 %d개 드립니다.&quot;, fruit, cnt);

    return 0;
}</code></pre><p>결과
<img src="https://velog.velcdn.com/images/yeon_archive/post/95283ce4-23b3-43c7-bec5-58dac8cc2ceb/image.png" alt=""></p>
<ol start="3">
<li>키보드로 문자를 입력해 아스키 코드를 출력하는 프로그램이 완성되도록
빈칸에 알맞은 코드 작성</li>
</ol>
<p>코드</p>
<pre><code>int main(void)
{
    char ch; // 문자를 저장할 변수

    printf(&quot;문자 입력: &quot;); // 입력 안내 메시지
    scanf(&quot;%c&quot;, &amp;ch); // 변수 ch에 문자 입력
    printf(&quot;%c문자의 아스키 코드 값은 %d입니다.\n&quot;, ch, ch); // 변환해서 출력

    return 0;
}</code></pre><p>결과
<img src="https://velog.velcdn.com/images/yeon_archive/post/792e6479-e0fe-43bf-a244-db030b43c8e8/image.png" alt=""></p>
<h3 id="4-2-그-외-유용한-연산자-p118-119">4-2 그 외 유용한 연산자 (P.118-119)</h3>
<ol>
<li>short형과 long형의 자료형 크기를 비교해 크기가 큰 자료형을 출력하는 프로그램이 완성되도록 빈칸 채우기</li>
</ol>
<p>코드</p>
<pre><code>int main(void)
{
    int res; // 결과 변수

    if (sizeof(short) &gt; sizeof(long)) {
        res = 1;
    } else {
        res = 0;
    }

    // sizeof의 피연산자로 자료형 이름을 사용해 크기를 바이트 단위로 계산한다.
    // short형의 크기가 long형보다 크면 참이므로 1, 그렇지 않으면 0을 res에 저장한다.


    if (res == 1) {
        printf(&quot;short\n&quot;);
    } else {
        printf(&quot;long\n&quot;);
    }

    return 0;

    // res가 1과 같으면 short형의 크기가 크므로 출력하고
    // 그렇지 않으면 long형을 출력한다.
}

</code></pre><p>결과
<img src="https://velog.velcdn.com/images/yeon_archive/post/7d1f54c1-f282-4d04-85e2-9e85f560d0db/image.png" alt=""></p>
<ol start="2">
<li>야구경기장의 좌석 수 70개, 입장객 수가 65명일 때 입장률 표시되는 프로그램이 완성되도록 빈칸 채우기</li>
</ol>
<p>코드</p>
<pre><code>int main(void)
{
    int seats = 70;      // 경기장의 좌석 수(seats) 초기화
    int audience = 65;   // 입장객 수(audience) 초기화
    double rate;         // 입장률(rate)을 저장할 변수

    // &#39;audience / seats&#39;를 바로 연산하면 둘 다 int형이므로 몫을 계산한다.
    // 이 경우 입장객 수가 좌석 수보다 크지 않으므로 항상 0이 출력된다.
    // 따라서 소수점까지 계산할 수 있도록 double형으로 형 변환한다.
    // 나누기(/)와 곱하기(*)는 우선순위가 같으므로 연산 방향에 따라 왼쪽부터
    // 나누기 연산이 먼저 수행된다.
    rate = (double)audience / seats * 100;
    printf(&quot;입장률: %.1f%%\n&quot;, rate); // 입장률 출력

    return 0;
}</code></pre><p>결과
<img src="https://velog.velcdn.com/images/yeon_archive/post/f5fee974-11e9-4c93-aa64-32f981b99424/image.png" alt=""></p>
<ol start="3">
<li>3.76시간은 몇 시간, 몇 분, 몇 초인지 출력하는 프로그램이 되도록 빈칸 채우기</li>
</ol>
<p>코드</p>
<pre><code>int main(void)
{
    int hour, min, sec;        // 시, 분, 초를 저장할 변수
    double time = 3.76;        // 시간 초기화

    hour = (int)time;          // 형 변환으로 정수 부분만을 골라낸다.
    time = (time - hour) * 60; // 한 시간이 안 되는 부분만을 다시 저장한다.
    min = (int)time;           // 분 단위로 환산
    time = (time - min) * 60;  // 일분이 안 되는 부분만을 다시 저장한다.
    sec = (int)time;           // 초 단위로 환산

    printf(&quot;%.2f시간은 %d시간 %d분 %d초입니다.\n&quot;, 3.76, hour, min, sec); // 변환한 시간 출력

    return 0;
}</code></pre><p>결과
<img src="https://velog.velcdn.com/images/yeon_archive/post/e51fccf1-781d-4f4f-80aa-314a8a9b8311/image.png" alt=""></p>
<p>감사합니다 (●&#39;◡&#39;●)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[혼공파] 1주차_다시 c 공부하기]]></title>
            <link>https://velog.io/@yeon_archive/%ED%98%BC%EA%B3%B5%ED%8C%8C-1%EC%A3%BC%EC%B0%A8%EB%8B%A4%EC%8B%9C-c-%EA%B3%B5%EB%B6%80%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@yeon_archive/%ED%98%BC%EA%B3%B5%ED%8C%8C-1%EC%A3%BC%EC%B0%A8%EB%8B%A4%EC%8B%9C-c-%EA%B3%B5%EB%B6%80%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 06 Jul 2024 13:04:03 GMT</pubDate>
            <description><![CDATA[<p>한창 웹, 앱 하다가 인공지능 하다가 파이썬 하느라 c를 까먹었는데..!
어떤 분이 혼공 링크를 공유해 주셔서 냅다 신청했다
안 그래도 이번 방학엔 다시 하고 싶었는데 ~ (❁´◡`❁)🍀
계절학기 이슈로 다음 주까지는 기본 과제만 하겠지만.. 
그래도 조와.. </p>
<p>[기본 과제]</p>
<p>근본 코드로 설치 인증합니다
<img src="https://velog.velcdn.com/images/yeon_archive/post/e2c969d6-54be-452d-8c1e-d7ddc313ba60/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yeon_archive/post/4c746a05-7917-4eb1-a266-553ca2f4441a/image.png" alt=""></p>
<p>무조건적인 박수갈채, 일방적이고 편향적인 칭찬 부탁드립니다 </p>
<p><img src="https://velog.velcdn.com/images/yeon_archive/post/1a1e61e2-5e6e-426b-9bfb-db9ecc67fcf9/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[주파수를 이용한 알람 앱]]></title>
            <link>https://velog.io/@yeon_archive/%EC%A3%BC%ED%8C%8C%EC%88%98%EC%95%8C%EB%9E%8C</link>
            <guid>https://velog.io/@yeon_archive/%EC%A3%BC%ED%8C%8C%EC%88%98%EC%95%8C%EB%9E%8C</guid>
            <pubDate>Thu, 06 Jun 2024 18:13:15 GMT</pubDate>
            <description><![CDATA[<h2 id="✨apk-다운로드-링크는-아래에">✨APK 다운로드 링크는 아래에</h2>
<h3 id="알람-앱-제작">알람 앱 제작</h3>
<p>주파수를 알람에 활용해보자는 생각에서 진행하게 된 프로젝트!</p>
<h3 id="주파수를-어떻게-사용했을까">주파수를 어떻게 사용했을까?</h3>
<p><img src="https://velog.velcdn.com/images/yeon_archive/post/1393c438-fff3-4a9f-aa2b-2e096e45e415/image.png" alt="">
▲ 중앙일보 기사 캡쳐</p>
<p>2000~5000Hz를 이용해 파이썬으로 주파수 코드를 제작</p>
<h3 id="사용한-툴">사용한 툴</h3>
<p>파이썬(주파수 코드), 리액트 네이티브(앱 개발)</p>
<p>파이썬은 보통 백엔드에 사용하지만 이번에는 코드 작성에 사용했다.
리액트 네이티브는 크로스 플랫폼이라는 장점과 단기간에 개발하기 좋아 사용하게 되었다.</p>
<h3 id="주파수-코드">주파수 코드</h3>
<p>생각했던 것만큼 오래 걸리지 않았고, 
간단하게 만들 수 있어서 앱까지 만들게 되었다.</p>
<pre><code>## 파이썬 코드
import os
import numpy as np
import soundfile as sf
import wave

## 알람 코드

def generate_alarm(high_frequency, duration=5, directory=&quot;.&quot;, filename=&quot;alarm.wav&quot;):
    # 저주파 설정(Hz)
    low_frequency = 100

    sample_rate = 44100

    # 시간 배열
    t = np.linspace(0, duration, int(duration * sample_rate), endpoint=False)

    # 저주파, 고주파 사인파 생성
    low_signal = 0.5 * np.sin(2 * np.pi * low_frequency * t)
    high_signal = 0.5 * np.sin(2 * np.pi * high_frequency * t)

    # 저주파, 고주파 합성
    alarm_signal = low_signal + high_signal

    # 16bit 변환
    alarm_signal = np.int16(alarm_signal * 32767)

    # 파일 경로
    file_path = os.path.join(directory, filename)
    if not os.path.exists(directory):
        os.makedirs(directory)

    with wave.open(file_path, &#39;wb&#39;) as wav_file:
        wav_file.setnchannels(1)
        wav_file.setsampwidth(2)
        wav_file.setframerate(sample_rate)
        wav_file.writeframes(alarm_signal.tobytes())



    # # 재생
    # sd.play(alarm_signal, samplerate=44100)
    # sd.wait()

    # # 파일로 저장
    # sf.write(filename, alarm_signal, 44100)

#알람 파일
saved_directory = &#39;./alarm_signals&#39;
generate_alarm(2000, directory=saved_directory, filename=&quot;alarm_2000.wav&quot;)
generate_alarm(3000, directory=saved_directory, filename=&quot;alarm_3000.wav&quot;)
generate_alarm(4000, directory=saved_directory, filename=&quot;alarm_4000.wav&quot;)
generate_alarm(5000, directory=saved_directory, filename=&quot;alarm_5000.wav&quot;)
</code></pre><h4 id="전체-앱-기능">전체 앱 기능</h4>
<p><img src="https://velog.velcdn.com/images/yeon_archive/post/ac5337ac-41aa-4c1c-bab9-4cd085c93dda/image.png" alt=""></p>
<p>메인 페이지에서 알람을 설정하려면 + 표시를 눌러야 하고, </p>
<ul>
<li>표시 누르면 Setting.js로 이동한다.</li>
</ul>
<p>알람 설정해둔 시간에 알람 울리면서 AlarmScreen.js으로 이동
&quot;끄기&quot; 버튼을 누르면 메인 페이지로 이동한다.</p>
<p>*리액트 네이티브 특성상 사용자가 앱 내 어느 페이지에 위치해 있는지 알지 못해서 setAlarm.js에서 알람이 울리는 오류가 발생
currentRoute 변수를 사용해 알람 설정 페이지에서는 울리지 않도록 처리함 </p>
<h3 id="앱-디자인">앱 디자인</h3>
<p><img src="https://velog.velcdn.com/images/yeon_archive/post/3ac65177-debe-4b52-a3af-04cbef8a141a/image.png" alt=""></p>
<p>이미 만들어진 색상 팔레트 값 사용 (출처 사진에 있음)</p>
<h4 id="메인-페이지">메인 페이지</h4>
<p><img src="https://velog.velcdn.com/images/yeon_archive/post/63610faf-69a2-438b-b3be-024dca4ae7c2/image.png" alt=""></p>
<pre><code>// 스타일 태그 제외

// 알람 메인화면
export default function MainPage({ navigation }) {
    const [alarms, setAlarms] = useState([]);
    const isAlarmPlaying = useRef(false);
    const hasStoppedAlarm = useRef(false);
    const soundRefs = useRef([]);
    const isFocused = useIsFocused();
    const currentRouteName = useNavigationState(state =&gt; state.routes[state.index].name);

    // 수정하려고 클릭하면 알람 재생되는 오류 발생함
    // + 수정할 때만 그런게 아니라 AlarmScreen.js에서 MainPage.js로 넘어와도 소리남

    // 앱 내 위치 표시하고 1초마다 console 띄우기
    // SetAlarm.js 들어갈 때 알람 재생 안 되도록 설정
    // MainPage.js에서는 사운드 파일 언로드

    // 날짜, 요일 확인 후에 알람 실행되도록

    useFocusEffect(
        useCallback(() =&gt; {
            loadAlarms();
            checkAlarmStopped();
            return () =&gt; resetAlarm();
        }, [])
    );


    // 알람 불러오기
    const loadAlarms = async () =&gt; {
        try {
            const storedAlarms = await AsyncStorage.getItem(&#39;alarms&#39;);
            if (storedAlarms !== null) {
                setAlarms(JSON.parse(storedAlarms));
            }
        } catch (error) {
            console.error(error);
        }
    };


    // AlarmScreen.js에서 MainPage.js로 이동 시 ,
    // 사운드 파일 종료 안 되는 오류 발생
    // 끄기 버튼 눌렀으면 사운드 꺼졌는지 확인
    const checkAlarmStopped = async () =&gt; {
        const alarmStopped = await AsyncStorage.getItem(&#39;alarmStopped&#39;);
        if (alarmStopped === &#39;true&#39;) {
            isAlarmPlaying.current = false;
            hasStoppedAlarm.current = true;
            await AsyncStorage.removeItem(&#39;alarmStopped&#39;);
            stopSound();
        }
    };

    const stopSound = async () =&gt; {
        try {
            for (let sound of soundRefs.current) {
                if (sound) {
                    await sound.stopAsync();
                    await sound.unloadAsync();
                }
            }
            soundRefs.current = [];
        } catch (error) {
            console.error(&#39;Error stopping sound:&#39;, error);
        } finally {
            isAlarmPlaying.current = false;
        }
    };


    // 알람 옆 토글 스위치 (on/off)
    const toggleSwitch = async (id) =&gt; {
        const updatedAlarms = alarms.map((alarm) =&gt;
            alarm.id === id ? { ...alarm, isEnabled: !alarm.isEnabled } : alarm
        );
        setAlarms(updatedAlarms);
        try {
            await AsyncStorage.setItem(&#39;alarms&#39;, JSON.stringify(updatedAlarms));
        } catch (error) {
            console.error(error);
        }

        const alarm = updatedAlarms.find(alarm =&gt; alarm.id === id);
        if (!alarm.isEnabled) {
            handleStopAlarm();
        }
    };

    const parseTime = (timeString) =&gt; {
        try {
            const [period, time] = timeString.split(&#39; &#39;);
            let [hour, minute] = time.split(&#39;:&#39;).map(Number);

            if (period === &#39;오후&#39; &amp;&amp; hour &lt; 12) {
                hour += 12;
            } else if (period === &#39;오전&#39; &amp;&amp; hour === 12) {
                hour = 0;
            }

            return { hour, minute };
        } catch (error) {
            console.error(&#39;시간 파싱 오류:&#39;, error, &#39;문자열:&#39;, timeString);
            return { hour: 0, minute: 0 };
        }
    };

    const checkAlarms = async () =&gt; {
        if (currentRouteName === &#39;SetAlarm&#39; || currentRouteName === &#39;AlarmScreen&#39;) {
            return;
        }

        const now = new Date();
        alarms.forEach(alarm =&gt; {
            if (alarm.isEnabled &amp;&amp; !isAlarmPlaying.current &amp;&amp; !hasStoppedAlarm.current) {
                const { hour, minute } = parseTime(alarm.time);
                if (now.getHours() === hour &amp;&amp; now.getMinutes() === minute) {
                    isAlarmPlaying.current = true;
                    navigation.navigate(&#39;AlarmScreen&#39;);
                }
            }
        });
    };


    // 알람 꺼졌는지 확인!!!
    const handleStopAlarm = async () =&gt; {
        isAlarmPlaying.current = false;
        hasStoppedAlarm.current = true;
        await AsyncStorage.setItem(&#39;alarmStopped&#39;, &#39;true&#39;);
        stopSound();
    };


    // 알람 한번 실행되면 다음 설정한 알람 안 울리는 오류 발생
    // 알람 리셋
    const resetAlarm = () =&gt; {
        isAlarmPlaying.current = false;
        hasStoppedAlarm.current = false;
    };

    useEffect(() =&gt; {
        const interval = setInterval(() =&gt; {
            if (isFocused) {
                checkAlarms();
            }
        }, 1000);
        return () =&gt; clearInterval(interval);
    }, [alarms, isFocused, currentRouteName]);

    useEffect(() =&gt; {
        const interval = setInterval(() =&gt; {
            if (isFocused) {
                checkAlarms();
            }
        }, 1000); 
        return () =&gt; clearInterval(interval);
    }, [alarms, isFocused]);

    return (
        &lt;View style={styles.container}&gt;
            &lt;FlatList
                data={alarms}
                renderItem={({ item }) =&gt; (
                    &lt;TouchableOpacity onPress={() =&gt; navigation.navigate(&#39;SetAlarm&#39;, { alarmId: item.id })}&gt;
                        &lt;View style={styles.alarmContainer}&gt;
                            &lt;View&gt;
                                &lt;Text style={styles.alarmText}&gt;{item.time}&lt;/Text&gt;
                                &lt;Text style={styles.alarmName}&gt;{item.name}&lt;/Text&gt;
                                &lt;Text style={styles.alarmDays}&gt;{(item.days || []).join(&#39;, &#39;)}&lt;/Text&gt;
                            &lt;/View&gt;
                            &lt;Switch
                                trackColor={{ false: &quot;#fff&quot;, true: &quot;#faf2ea&quot; }}
                                thumbColor={item.isEnabled ? &quot;#dba69e&quot; : &quot;#faf2ea&quot;}
                                ios_backgroundColor=&quot;#3e3e3e&quot;
                                value={item.isEnabled}
                                onValueChange={() =&gt; toggleSwitch(item.id)}
                            /&gt;
                        &lt;/View&gt;
                    &lt;/TouchableOpacity&gt;
                )}
                keyExtractor={(item) =&gt; item.id}
            /&gt;
            &lt;TouchableOpacity style={styles.addButton} onPress={() =&gt; navigation.navigate(&#39;Setting&#39;)}&gt;
                &lt;Text style={styles.addButtonText}&gt;+&lt;/Text&gt;
            &lt;/TouchableOpacity&gt;
        &lt;/View&gt;
    );
}</code></pre><h4 id="알람-페이지">알람 페이지</h4>
<p>알람 울릴 때 보여지는 페이지
<img src="https://velog.velcdn.com/images/yeon_archive/post/2ee14d31-bc62-474b-a8b2-8384545b4992/image.png" alt=""></p>
<pre><code>// 스타일 태그 제외

// 알람 울리면 나타나는 화면
export default function AlarmScreen({ navigation }) {
    const soundRef = useRef([]);
    const [isStopping, setIsStopping] = useState(false);

    useEffect(() =&gt; {
        playSound();
        return () =&gt; {
            stopSound();
        };
    }, []);

    //assets에 있는 알람 파일 load -&gt; 실행
    const playSound = async () =&gt; {
        const soundFiles = [
            require(&#39;./assets/alarm_2000.wav&#39;),
            require(&#39;./assets/alarm_3000.wav&#39;),
            require(&#39;./assets/alarm_4000.wav&#39;),
            require(&#39;./assets/alarm_5000.wav&#39;)
        ];

        try {
            for (const soundFile of soundFiles) {
                for (let i = 0; i &lt; 4; i++) {
                    if (isStopping) return;
                    const { sound } = await Audio.Sound.createAsync(soundFile);
                    soundRef.current.push(sound);
                    await sound.playAsync();
                    await new Promise(resolve =&gt; setTimeout(resolve, 5000));
                    if (isStopping) {
                        await sound.stopAsync();
                        await sound.unloadAsync();
                        return;
                    }
                    await sound.stopAsync();
                    await sound.unloadAsync();
                }
            }
        } catch (error) {
            console.error(&#39;Sound playback error:&#39;, error);
        }
    };


    // 사운드 파일 멈추기
    const stopSound = async () =&gt; {
        setIsStopping(true);
        for (let sound of soundRef.current) {
            if (sound &amp;&amp; sound._loaded) {
                try {
                    await sound.stopAsync();
                    await sound.unloadAsync();
                } catch (error) {
                    console.error(&#39;Error:&#39;, error);
                }
            }
        }
        soundRef.current = [];
    };


    // 사운드 파일 stop 시키면서 메인으로 이동
    const handleStopAlarm = async () =&gt; {
        await stopSound();
        await AsyncStorage.setItem(&#39;alarmStopped&#39;, &#39;true&#39;);
        navigation.goBack();
    };

    return (
        &lt;View style={styles.container}&gt;
            &lt;Text style={styles.alarmText}&gt;알람 울리는 중!&lt;/Text&gt;
            &lt;TouchableOpacity style={styles.btn} onPress={handleStopAlarm}&gt;
                &lt;Text style={styles.btnText}&gt;끄기&lt;/Text&gt;
            &lt;/TouchableOpacity&gt;
        &lt;/View&gt;
    );
}</code></pre><h4 id="편집수정-페이지">편집(수정) 페이지</h4>
<p>금요일 선택한 상태, 제목 작성함
<img src="https://velog.velcdn.com/images/yeon_archive/post/bafc0690-95c1-483e-880c-40bcac0b7a57/image.png" alt=""></p>
<pre><code>// 스타일 태그 제외

// 알람 수정하는 페이지
export default function SetAlarm({ route = {}, navigation }) {
  const { alarmId } = route.params || {};
  const [time, setTime] = useState(new Date());
  const [days, setDays] = useState([]);
  const [name, setName] = useState(&#39;&#39;);
  const [isDatePickerVisible, setDatePickerVisibility] = useState(false);
  const dayOptions = [&#39;일&#39;, &#39;월&#39;, &#39;화&#39;, &#39;수&#39;, &#39;목&#39;, &#39;금&#39;, &#39;토&#39;];

  useEffect(() =&gt; {
    loadAlarm(alarmId);
  }, [alarmId]);

  const loadAlarm = async (id) =&gt; {
    try {
      const storedAlarms = await AsyncStorage.getItem(&#39;alarms&#39;);
      if (storedAlarms !== null) {
        const alarms = JSON.parse(storedAlarms);
        const alarm = alarms.find((alarm) =&gt; alarm.id === id);
        if (alarm) {
          const [ampm, timeString] = alarm.time.split(&#39; &#39;);
          const [hours, minutes] = timeString.split(&#39;:&#39;).map(Number);
          const date = new Date();
          date.setHours(ampm === &#39;오후&#39; ? hours + 12 : hours, minutes);
          setTime(date);
          setDays(alarm.days || []);
          setName(alarm.name || &#39;&#39;);
        }
      }
    } catch (error) {
      console.error(error);
    }
  };

  const showDatePicker = () =&gt; {
    setDatePickerVisibility(true);
  };

  const hideDatePicker = () =&gt; {
    setDatePickerVisibility(false);
  };

  const handleConfirm = (selectedTime) =&gt; {
    setTime(selectedTime);
    hideDatePicker();
  };

  const formatTime = (date) =&gt; {
    let hours = date.getHours();
    const minutes = date.getMinutes();
    const ampm = hours &gt;= 12 ? &#39;오후&#39; : &#39;오전&#39;;
    hours = hours % 12;
    hours = hours ? hours : 12;
    const strTime = `${ampm} ${hours}:${minutes &lt; 10 ? `0${minutes}` : minutes}`;
    return strTime;
  };

  const toggleDay = (day) =&gt; {
    setDays((prevDays) =&gt;
      prevDays.includes(day) ? prevDays.filter((d) =&gt; d !== day) : [...prevDays, day]
    );
  };

  const saveAlarm = async () =&gt; {
    try {
      const newAlarm = {
        id: alarmId,
        time: formatTime(time),
        days: days,
        name: name,
        isEnabled: false,
      };
      const storedAlarms = await AsyncStorage.getItem(&#39;alarms&#39;);
      const alarms = storedAlarms ? JSON.parse(storedAlarms) : [];
      const updatedAlarms = alarms.map((alarm) =&gt; (alarm.id === alarmId ? newAlarm : alarm));
      await AsyncStorage.setItem(&#39;alarms&#39;, JSON.stringify(updatedAlarms));
      navigation.navigate(&#39;Main&#39;);
    } catch (error) {
      console.error(error);
    }
  };

  const deleteAlarm = async () =&gt; {
    try {
      const storedAlarms = await AsyncStorage.getItem(&#39;alarms&#39;);
      if (storedAlarms !== null) {
        const alarms = JSON.parse(storedAlarms);
        const updatedAlarms = alarms.filter((alarm) =&gt; alarm.id !== alarmId);
        await AsyncStorage.setItem(&#39;alarms&#39;, JSON.stringify(updatedAlarms));
        navigation.navigate(&#39;Main&#39;);
      }
    } catch (error) {
      console.error(error);
    }
  };

  return (
    &lt;ScrollView style={styles.container}&gt;
      &lt;Text style={styles.label}&gt;시간:&lt;/Text&gt;
      &lt;TouchableOpacity onPress={showDatePicker} style={styles.pickerButton}&gt;
        &lt;Text style={styles.pickerButtonText}&gt;{formatTime(time)}&lt;/Text&gt;
      &lt;/TouchableOpacity&gt;
      &lt;DateTimePickerModal
        isVisible={isDatePickerVisible}
        mode=&quot;time&quot;
        onConfirm={handleConfirm}
        onCancel={hideDatePicker}
      /&gt;

      &lt;Text style={styles.label}&gt;요일:&lt;/Text&gt;
      &lt;View style={styles.dayContainer}&gt;
        {dayOptions.map((day) =&gt; (
          &lt;TouchableOpacity
            key={day}
            style={[styles.dayButton, days.includes(day) &amp;&amp; styles.selectedDayButton]}
            onPress={() =&gt; toggleDay(day)}
          &gt;
            &lt;Text style={[styles.dayText, days.includes(day) &amp;&amp; styles.selectedDayText]}&gt;{day}&lt;/Text&gt;
          &lt;/TouchableOpacity&gt;
        ))}
      &lt;/View&gt;

      &lt;Text style={styles.label}&gt;알람 이름:&lt;/Text&gt;
      &lt;TextInput
        style={styles.textInput}
        value={name}
        onChangeText={setName}
        placeholder=&quot;알람 이름&quot;
      /&gt;

      &lt;View style={styles.buttonContainer}&gt;
        &lt;TouchableOpacity style={[styles.button, styles.deleteButton]} onPress={deleteAlarm}&gt;
            &lt;Text style={styles.buttonText}&gt;삭제&lt;/Text&gt;
        &lt;/TouchableOpacity&gt;
        &lt;TouchableOpacity style={styles.button} onPress={saveAlarm}&gt;
            &lt;Text style={styles.buttonText}&gt;저장&lt;/Text&gt;
        &lt;/TouchableOpacity&gt;
      &lt;/View&gt;
    &lt;/ScrollView&gt;
  );
}
</code></pre><h4 id="설정-페이지">설정 페이지</h4>
<p><img src="https://velog.velcdn.com/images/yeon_archive/post/8d7725cd-6258-4565-bc4f-4d0fd497010a/image.png" alt=""></p>
<pre><code>// 스타일 태그 제외

// 알람 생성하는 페이지
// 라이브러리 사용함
export default function Setting({ navigation }) {
  const [time, setTime] = useState(new Date());
  const [days, setDays] = useState([]);
  const [name, setName] = useState(&#39;&#39;);
  const [isDatePickerVisible, setDatePickerVisibility] = useState(false);
  const dayOptions = [&#39;일&#39;, &#39;월&#39;, &#39;화&#39;, &#39;수&#39;, &#39;목&#39;, &#39;금&#39;, &#39;토&#39;];

  const showDatePicker = () =&gt; {
    setDatePickerVisibility(true);
  };

  const hideDatePicker = () =&gt; {
    setDatePickerVisibility(false);
  };

  const handleConfirm = (selectedTime) =&gt; {
    setTime(selectedTime);
    hideDatePicker();
  };

  const formatTime = (date) =&gt; {
    let hours = date.getHours();
    const minutes = date.getMinutes();
    const ampm = hours &gt;= 12 ? &#39;오후&#39; : &#39;오전&#39;;
    hours = hours % 12;
    hours = hours ? hours : 12; 
    const strTime = `${ampm} ${hours}:${minutes &lt; 10 ? `0${minutes}` : minutes}`;
    return strTime;
  };

  const toggleDay = (day) =&gt; {
    setDays((prevDays) =&gt;
      prevDays.includes(day) ? prevDays.filter((d) =&gt; d !== day) : [...prevDays, day]
    );
  };


  // 알람 저장
  const saveAlarm = async () =&gt; {
    try {
      const newAlarm = {
        id: Date.now().toString(),
        time: formatTime(time),
        days: days,
        name: name,
        isEnabled: false,
      };
      const storedAlarms = await AsyncStorage.getItem(&#39;alarms&#39;);
      const alarms = storedAlarms ? JSON.parse(storedAlarms) : [];
      alarms.push(newAlarm);
      await AsyncStorage.setItem(&#39;alarms&#39;, JSON.stringify(alarms));
      navigation.navigate(&#39;Main&#39;);
    } catch (error) {
      console.error(error);
    }
  };

  return (
    &lt;ScrollView style={styles.container}&gt;
      &lt;Text style={styles.label}&gt;시간:&lt;/Text&gt;
      &lt;TouchableOpacity onPress={showDatePicker} style={styles.pickerButton}&gt;
        &lt;Text style={styles.pickerButtonText}&gt;{formatTime(time)}&lt;/Text&gt;
      &lt;/TouchableOpacity&gt;
      &lt;DateTimePickerModal
        isVisible={isDatePickerVisible}
        mode=&quot;time&quot;
        onConfirm={handleConfirm}
        onCancel={hideDatePicker}
      /&gt;

      &lt;Text style={styles.label}&gt;요일:&lt;/Text&gt;
      &lt;View style={styles.dayContainer}&gt;
        {dayOptions.map((day) =&gt; (
          &lt;TouchableOpacity
            key={day}
            style={[styles.dayButton, days.includes(day) &amp;&amp; styles.selectedDayButton]}
            onPress={() =&gt; toggleDay(day)}
          &gt;
            &lt;Text style={[styles.dayText, days.includes(day) &amp;&amp; styles.selectedDayText]}&gt;{day}&lt;/Text&gt;
          &lt;/TouchableOpacity&gt;
        ))}
      &lt;/View&gt;

      &lt;Text style={styles.label}&gt;알람 이름:&lt;/Text&gt;
      &lt;TextInput
        style={styles.textInput}
        value={name}
        onChangeText={setName}
        placeholder=&quot;알람 이름 입력&quot;
      /&gt;

      &lt;View style={styles.buttonContainer}&gt;
        &lt;TouchableOpacity style={styles.button} onPress={() =&gt; navigation.navigate(&#39;Main&#39;)}&gt;
          &lt;Text style={styles.buttonText}&gt;취소&lt;/Text&gt;
        &lt;/TouchableOpacity&gt;
        &lt;TouchableOpacity style={styles.button} onPress={saveAlarm}&gt;
          &lt;Text style={styles.buttonText}&gt;확인&lt;/Text&gt;
        &lt;/TouchableOpacity&gt;
      &lt;/View&gt;
    &lt;/ScrollView&gt;
  );
}</code></pre><h4 id="앱-아이콘">앱 아이콘</h4>
<p><img src="https://velog.velcdn.com/images/yeon_archive/post/46757ca1-abaa-4bdc-9177-a6c2228cfd28/image.png" alt="">
졸린 고양이
apk로 다운 받았을 때는 기본 아이콘이 뜬다
이것도 수정해야지</p>
<h4 id="aab-파일">aab 파일</h4>
<p>expo 배포는 처음이라 헤매다가 당연히 apk일 줄 알고 앱 생성했는데..!
aab라서 파일 변환시켰다.
이때 jdk가 필요하다는 걸 새롭게 알게 됨</p>
<h3 id="마무리">마무리</h3>
<p>이건 추가하고 싶었던 기능들인데 아쉽게도 오랜만에 리액트를 사용하다보니
오류랑 버그 해결하는데 시간을 많이 투자해서 넣지 못했다.
방학 때 추가해보고 구글플레이에 업로드까지 해볼 생각!</p>
<p><img src="https://velog.velcdn.com/images/yeon_archive/post/afbdf535-9b64-4474-9c9b-0e81c965eab4/image.png" alt=""></p>
<h2 id="apk-파일-링크">APK 파일 링크</h2>
<p><a href="https://drive.google.com/file/d/1OqnRrFcCcv72-vHXqNs71x5uEAW2Osjm/view?usp=drive_link">링크텍스트</a>
구글 드라이브에서 다운로드 가능!</p>
]]></description>
        </item>
    </channel>
</rss>